Skip to contentSkip to conversation

FireWire/SBP2 Target mode

RFC PATCH Superseded Chris Boot 8 participants 100 messages Aug 17, 2011 – Apr 18, 2012 1 message not in the archive Page 1 of 2

Subsystems (4)
lore
Share & tools
lore

Chris Boot

author maintainer
#0 lore raw

Hi folks,

Now that we have a nice SCSI target framework in the kernel, and drivers either in-tree or being worked on for iSCSI, FC, FCoE, SAS and iSER, I was wondering about the possibility of adding FireWire to that list. Apple Macs have had a FireWire Target Disk mode for a long time, and there is even a program called Endpoint[1] that apparently does this on Linux though it looks completely untouched since 2003 and no longer works as it depends on the old FireWire stack.

I would very much like to see something like this added to the kernel, and would very much like to contribute to writing this feature. However my kernel development knowledge is very limited indeed so this would require a lot hand-holding and mentoring.

What's the interest in such a feature? Would anyone be able to give some (a lot?) of their time to help write this?

Thanks,
Chris

[1] http://oss.oracle.com/projects/endpoint/

------------------------------------------------------------------------------
Get a FREE DOWNLOAD! and learn more about uberSVN rich system, user administration capabilities and model configuration. Take the hassle out of deploying and managing Subversion and the tools developers use with it. http://p.sf.net/sfu/wandisco-d2d-2

#1 lore raw
Quoted 4 lines · On Aug 17 Chris Boot wrote:

Now that we have a nice SCSI target framework in the kernel, and drivers either in-tree or being worked on for iSCSI, FC, FCoE, SAS and iSER, I was wondering about the possibility of adding FireWire to that list. Apple Macs have had a FireWire Target Disk mode for a long time, and

Side note: This target mode is a feature of their OpenFirmware and EFI firmwares, not of the Mac OS and the OS X. Though both firmware- and operating-system-implemented SBP-2 targets certainly have their use cases. Aside from the SBP-2 target in Macs and from Oracle Endpoint, I only know of SBP-2 targets that are built upon dedicated bus bridge silicon.

Quoted 3 lines

there is even a program called Endpoint[1] that apparently does this on Linux though it looks completely untouched since 2003 and no longer works as it depends on the old FireWire stack.

It can be made to work again if it and libraw1394 are slightly extended to use a different API for Configuration ROM changes. Or Endpoint could be ported from libraw1394 to <linux/firewire-cdev.h>. But then it would of course not benefit from the generic SCSI target infrastructure, e.g. WRT administration, backing storage choices, or command set support.

Quoted 6 lines

I would very much like to see something like this added to the kernel, and would very much like to contribute to writing this feature. However my kernel development knowledge is very limited indeed so this would require a lot hand-holding and mentoring.

What's the interest in such a feature?

People keep asking for it; very infrequently though.

Quoted 7 lines

Would anyone be able to give some (a lot?) of their time to help write this?

Thanks,
Chris

[1] http://oss.oracle.com/projects/endpoint/

I for one can answer questions about firewire-core's driver API but do not have much spare time for more, for the time being.

BTW, a kernelspace SBP-2 target drivers would use several of the FireWire features that the firewire-net driver uses too. So the sources of that driver may be of help where the firewire-core kerneldoc comments are missing or are not providing the bigger picture.

Signature · 3 lines
Stefan Richter
-=====-==-== =--- =---=
http://arcgraph.de/sr/
#2 lore raw
In reply to Chris Boot
Quoted 3 lines · Chris Boot

Now that we have a nice SCSI target framework in the kernel, and drivers either in-tree or being worked on for iSCSI, FC, FCoE, SAS and iSER, I was wondering about the possibility of adding FireWire to that list.

As far as I can tell from briefly looking at the framework, this would require both a fabric module (which is nicely abstracted) and a new protocol (which doesn't seem to be).

Is there any documentation of how to add a new LIO protocol?

And what is the difference between a protocol and a fabric?

Regards,
Clemens

#3 lore raw

Sorry to raise this thread from the dead.

Chris Boot wrote Aug 2011-ish:

Quoted 4 lines

I would very much like to see something like this added to the kernel, and would very much like to contribute to writing this feature. However my kernel development knowledge is very limited indeed so this would require a lot hand-holding and mentoring.

If you're still interested, I'd definitely encourage you to work on this project. Me and others on target-devel would be happy to help with any target-related advice.

Quoted 5 lines · Clemens Ladisch
Quoted 3 lines · Chris Boot

Now that we have a nice SCSI target framework in the kernel, and drivers either in-tree or being worked on for iSCSI, FC, FCoE, SAS and iSER, I was wondering about the possibility of adding FireWire to that list.

Quoted 7 lines

As far as I can tell from briefly looking at the framework, this would require both a fabric module (which is nicely abstracted) and a new protocol (which doesn't seem to be).

Is there any documentation of how to add a new LIO protocol?

And what is the difference between a protocol and a fabric?

Right now the documentation is the other fabric modules, alas.

You're saying the sbp2 protocol can theoretically be used other places besides firewire? I'd say put it all in the fabric module for now.

You can also join irc.oftc.net #storage and ask more questions there.

Regards -- Andy

#4 lore raw
Quoted 2 lines · On Feb 01 Andy Grover wrote:

You're saying the sbp2 protocol can theoretically be used other places besides firewire? I'd say put it all in the fabric module for now.

While SBP-2 is explicitly specified for IEEE 1394 a.k.a. FireWire only, it could be used on any bus architecture which implements IEEE 1212 "Control and Status Registers (CSR) Architecture for Microcomputer Buses". But IEEE 1394 a.k.a. FireWire is nowadays the only remaining IEEE 1212 implementation, as far as I know.

On the other hand, the transport protocol SBP-2 can be used to encapsulate various protocols besides SCSI. A long time ago I heard that there were SBP-2 printers that weren't SCSI printers, but I may have confused something.

The current version of the protocol, SBP-3 which extends SBP-2 in an upwards compatible way, has been developed specifically in order to transport AV/C command sets. (AV/C is a set of specifications from the 1394 Trade Association that build on top of IEC 61883 "Consumer audio/video equipment - Digital interface" and is used for camcorders, audio interfaces, studio-grade video equipment, TV set top boxes etc.) But I am not aware of any existing SBP-3 target or SBP-3 initiator which actually implements AV/C.

So in short,
- SBP-2/3 is only used on the FireWire bus.
- Potentially there could be SBP-2/3 targets or initiators that implement something else than SCSI. But stock SBP-2/3 initiator drivers of popular OSs are SCSI initiators, and non-SCSI SBP-2/3 targets are extremely rare or don't exist actually.

Signature · 10 lines
Stefan Richter
-=====-===-- --=- ----=
http://arcgraph.de/sr/

------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d
#5 lore raw
Quoted 10 lines · Stefan Richter
Quoted 2 lines · On Feb 01 Andy Grover wrote:

You're saying the sbp2 protocol can theoretically be used other places besides firewire? I'd say put it all in the fabric module for now.

While SBP-2 is explicitly specified for IEEE 1394 a.k.a. FireWire only, it could be used on any bus architecture which implements IEEE 1212 "Control and Status Registers (CSR) Architecture for Microcomputer Buses". But IEEE 1394 a.k.a. FireWire is nowadays the only remaining IEEE 1212 implementation, as far as I know.

Quoted 5 lines

On the other hand, the transport protocol SBP-2 can be used to encapsulate various protocols besides SCSI. A long time ago I heard that there were SBP-2 printers that weren't SCSI printers, but I may have confused something.

Hmm Interesting. So what do the other FireWire protocols use, like networking, DV video, audio? they have yet another encapsulating transport other than SBP-2? What a mess

Quoted 9 lines

The current version of the protocol, SBP-3 which extends SBP-2 in an upwards compatible way, has been developed specifically in order to transport AV/C command sets. (AV/C is a set of specifications from the 1394 Trade Association that build on top of IEC 61883 "Consumer audio/video equipment - Digital interface" and is used for camcorders, audio interfaces, studio-grade video equipment, TV set top boxes etc.) But I am not aware of any existing SBP-3 target or SBP-3 initiator which actually implements AV/C.

Quoted 6 lines

So in short,
- SBP-2/3 is only used on the FireWire bus.
- Potentially there could be SBP-2/3 targets or initiators that implement something else than SCSI. But stock SBP-2/3 initiator drivers of popular OSs are SCSI initiators, and non-SCSI SBP-2/3 targets are extremely rare or don't exist actually.

Thanks that was informative
I'll have a deeper look

Boaz

------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3, Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d

#6 lore raw
Quoted 2 lines · Boaz Harrosh

[...] So what do the other FireWire protocols use, like networking, DV video, audio?

Their own standard or non-standard mechanism.

Quoted 1 line

they have yet another encapsulating transport other than SBP-2?

These other devices require isochronous or broadcast transfers, which SBP-2/3 does not handle well or at all. It might be possible to use SBP-3 for control commands (which is exactly what SBP-3's AV/C support is), but using it would not bring any worthwhile benefits over existing transports, and would just introduce incompatibilities with all previous soft- and hardware.

Regards,
Clemens

------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3, Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d

author maintainer
#7 lore raw

Hi folks,

I've been forging ahead in the last few days with my target code and have got to a point where it appears to work fairly well and now needs some real testing and ideally some initial review.

So far I've tested that I can export LUNs to Linux and Mac OS X machines and they can read/write data without issues as you might expect. I've even installed Mac OS X onto an exported LUN and booted from it on an old PowerBook without issue.

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

Or use GitHub to have a look etc...:
https://github.com/bootc/Linux-SBP-2-Target

I used the following script to set up a LUN on the target:

modprobe firewire-sbp-target
mkdir /sys/kernel/config/target/sbp
mkdir /sys/kernel/config/target/sbp/test
mkdir /sys/kernel/config/target/sbp/test/unit_0
mkdir /sys/kernel/config/target/sbp/test/unit_0/lun/lun_0
ln -s /sys/kernel/config/target/core/iblock_0/test /sys/kernel/config/target/sbp/test/unit_0/lun/lun_0/test echo 1 > /sys/kernel/config/target/sbp/test/unit_0/enable

Please note that you can't then disable a unit until all the targets are logged-out. For Linux this usually means 'rmmod firewire_sbp2'. This is one of the first things I'd like to fix but I'm not entirely sure how to go about it yet.

Thanks,
Chris

Signature · 2 lines
Chris Boot
[email protected]
#8 lore raw
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

SBP-2 says:
| The target shall issue data transfer requests with a speed equal to | that specified by the spd field in the ORB.

SBP-3 says:

| The target shall issue data transfer requests with a speed equal to
| that specified by the controlling spd field, whether in the ORB or in
| a node selector in an associated page table.
Quoted 2 lines

Please note that you can't then disable a unit until all the targets are logged-out. For Linux this usually means 'rmmod firewire_sbp2'.

That driver should not, by default, log into targets on its own node.

Regards,
Clemens

author maintainer
#9 lore raw
Quoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

(T10/1155D Revision 4 page 53/54)

Quoted 5 lines

SBP-2 says:
| The target shall issue data transfer requests with a speed equal to | that specified by the spd field in the ORB.

SBP-3 says:

Quoted 8 lines
| The target shall issue data transfer requests with a speed equal to
| that specified by the controlling spd field, whether in the ORB or in
| a node selector in an associated page table.
Quoted 2 lines

Please note that you can't then disable a unit until all the targets are logged-out. For Linux this usually means 'rmmod firewire_sbp2'.

That driver should not, by default, log into targets on its own node.

It still tries to and never appears to give up, so this needs blacklisting on the target system until firewire_sbp2 is changed. It's harmless other than filling up the kernel log with error messages.

What I meant, however, is if you connect to the target from a separate Linux system you will be unable to disable the unit on the target system until the _initiator_ logs out. You can do this on the initiator by unloading the module, which sends a logout request to the target.

HTH,
Chris

Signature · 2 lines
Chris Boot
[email protected]
#10 lore raw
Quoted 10 lines · On Feb 06 Chris Boot wrote:
Quoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

Quoted 8 lines

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

(T10/1155D Revision 4 page 53/54)

I guess it is not too hard to add this to the AR-req handler. On the other hand, I see little reason to follow the SBP-2 spec to the letter here. The target driver could just use the maximum speed that the core figured out. On the other hand, this requires of course
- the target to wait for core to finish scanning an initiator,
- the core to offer an API to look up an fw_device by a card--generation--nodeID tuple.

The intention of the spec is IMO clearly to enable target implementations that do not need to implement topology scanning. I have a hard time to think of a valid scenario where an initiator needs to be able to steer a target towards a lower wire speed than what the participating links and PHYs actually support.

Quoted 8 lines
Quoted 8 lines

SBP-2 says:
| The target shall issue data transfer requests with a speed equal to | that specified by the spd field in the ORB.

SBP-3 says:

| The target shall issue data transfer requests with a speed equal to
| that specified by the controlling spd field, whether in the ORB or in
| a node selector in an associated page table.

Clemens, the speed used in data transfers can be different from the speed to be used to read the initiator's Config ROM/ fetch ORBs/ write status, because data buffers and page tables can reside on a third node. (In practice they reside always on the initiator node, but per spec they don't have to.)

Again, IMO the target driver could ignore this other speed too and just use the speed which firewire-core figured out. But on the other hand, this would require an fw_device lookup, given card--generation--nodeID; so using the speed code given in the ORB is much simpler.

Side note: Like with the speed, the max_payload given in the ORB may be different from that in the initiator's bus information block, simply because the data buffers and page tables may reside on a third node with a different packet payload limit. Again, just using the max_payload given in the ORB makes for the easiest implementation (and follows the spec to the letter).

Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

Please note that you can't then disable a unit until all the targets are logged-out. For Linux this usually means 'rmmod firewire_sbp2'.

That driver should not, by default, log into targets on its own node.

Quoted 8 lines

It still tries to and never appears to give up, so this needs blacklisting on the target system until firewire_sbp2 is changed. It's harmless other than filling up the kernel log with error messages.

What I meant, however, is if you connect to the target from a separate Linux system you will be unable to disable the unit on the target system until the _initiator_ logs out. You can do this on the initiator by unloading the module, which sends a logout request to the target.

Another way to log out is

# echo "fw4.0" > /sys/bus/firewire/drivers/*sbp2/unbind

(I will post a patch soon which renames this directory from sbp2 to firewire_sbp2, as a side effect of a logging related change.)

Signature · 3 lines
Stefan Richter
-=====-===-- --=- --==-
http://arcgraph.de/sr/
author maintainer
#11 lore raw
Quoted 28 lines · Stefan Richter
Quoted 10 lines · On Feb 06 Chris Boot wrote:
Quoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

Quoted 8 lines

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

(T10/1155D Revision 4 page 53/54)

I guess it is not too hard to add this to the AR-req handler. On the other hand, I see little reason to follow the SBP-2 spec to the letter here. The target driver could just use the maximum speed that the core figured out. On the other hand, this requires of course
- the target to wait for core to finish scanning an initiator,
- the core to offer an API to look up an fw_device by a card--generation--nodeID tuple.

Quoted 5 lines

The intention of the spec is IMO clearly to enable target implementations that do not need to implement topology scanning. I have a hard time to think of a valid scenario where an initiator needs to be able to steer a target towards a lower wire speed than what the participating links and PHYs actually support.

The only thing stopping me from getting the speed is the fact that struct fw_request is opaque. The value is easily available from request->response.speed and I kind of do that already in a very hackish way. I've sent a separate patch which adds a function that can be used to access that one value.

Waiting until the bus scan is complete isn't actually that great as I see the first LOGIN requests often before the fw_node is seen at all. I'd have to turn away the requester and hope they try again. I'm fairly sure my little tweak in my patch is a simple enough solution.

Quoted 20 lines
Quoted 8 lines
Quoted 8 lines

SBP-2 says:
| The target shall issue data transfer requests with a speed equal to | that specified by the spd field in the ORB.

SBP-3 says:

| The target shall issue data transfer requests with a speed equal to
| that specified by the controlling spd field, whether in the ORB or in
| a node selector in an associated page table.

Clemens, the speed used in data transfers can be different from the speed to be used to read the initiator's Config ROM/ fetch ORBs/ write status, because data buffers and page tables can reside on a third node. (In practice they reside always on the initiator node, but per spec they don't have to.)

Again, IMO the target driver could ignore this other speed too and just use the speed which firewire-core figured out. But on the other hand, this would require an fw_device lookup, given card--generation--nodeID; so using the speed code given in the ORB is much simpler.

Quoted 6 lines

Side note: Like with the speed, the max_payload given in the ORB may be different from that in the initiator's bus information block, simply because the data buffers and page tables may reside on a third node with a different packet payload limit. Again, just using the max_payload given in the ORB makes for the easiest implementation (and follows the spec to the letter).

Hmm yes so far I've ignored the fact that the data and page tables can be on a different node. I should add that to the TODO fairly low down :-)

Quoted 14 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

Please note that you can't then disable a unit until all the targets are logged-out. For Linux this usually means 'rmmod firewire_sbp2'.

That driver should not, by default, log into targets on its own node.

Quoted 8 lines

It still tries to and never appears to give up, so this needs blacklisting on the target system until firewire_sbp2 is changed. It's harmless other than filling up the kernel log with error messages.

What I meant, however, is if you connect to the target from a separate Linux system you will be unable to disable the unit on the target system until the _initiator_ logs out. You can do this on the initiator by unloading the module, which sends a logout request to the target.

Quoted 5 lines

Another way to log out is

# echo "fw4.0" > /sys/bus/firewire/drivers/*sbp2/unbind

(I will post a patch soon which renames this directory from sbp2 to firewire_sbp2, as a side effect of a logging related change.)

Good to know, thanks!

HTH,
Chris

Signature · 2 lines
Chris Boot
[email protected]
#12 lore raw

Hi,

Quoted 36 lines · Chris Boot
Quoted 28 lines · Stefan Richter
Quoted 10 lines · On Feb 06 Chris Boot wrote:
Quoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

Quoted 8 lines

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

(T10/1155D Revision 4 page 53/54)

I guess it is not too hard to add this to the AR-req handler.  On the other hand, I see little reason to follow the SBP-2 spec to the letter here.  The target driver could just use the maximum speed that the core figured out.  On the other hand, this requires of course
 - the target to wait for core to finish scanning an initiator,
 - the core to offer an API to look up an fw_device by a card--generation--nodeID tuple.

Quoted 5 lines

The intention of the spec is IMO clearly to enable target implementations that do not need to implement topology scanning.  I have a hard time to think of a valid scenario where an initiator needs to be able to steer a target towards a lower wire speed than what the participating links and PHYs actually support.

Quoted 3 lines

The only thing stopping me from getting the speed is the fact that struct fw_request is opaque. The value is easily available from request->response.speed and I kind of do that already in a very hackish way. I've sent a separate patch which adds a function that can be used to access that one value.

Waiting until the bus scan is complete isn't actually that great as I see the first LOGIN requests often before the fw_node is seen at all. I'd have to turn away the requester and hope they try again. I'm fairly sure my little tweak in my patch is a simple enough solution.

Stupid question: Could you use a completion queue or something equivalent to wait until you have seen the fw_node, *then* process the LOGIN request?

Thanks,

Signature · 5 lines
Julian Calaby

Email: [email protected]
Profile: http://www.google.com/profiles/julian.calaby/
.Plan: http://sites.google.com/site/juliancalaby/
author maintainer
#13 lore raw
Quoted 39 lines · Julian Calaby

Hi,

Quoted 36 lines · Chris Boot
Quoted 33 lines · Stefan Richter
Quoted 10 lines · On Feb 06 Chris Boot wrote:
Quoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

Quoted 8 lines

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

(T10/1155D Revision 4 page 53/54)

I guess it is not too hard to add this to the AR-req handler. On the other hand, I see little reason to follow the SBP-2 spec to the letter here. The target driver could just use the maximum speed that the core figured out. On the other hand, this requires of course
- the target to wait for core to finish scanning an initiator,
- the core to offer an API to look up an fw_device by a card--generation--nodeID tuple.

The intention of the spec is IMO clearly to enable target implementations that do not need to implement topology scanning. I have a hard time to think of a valid scenario where an initiator needs to be able to steer a target towards a lower wire speed than what the participating links and PHYs actually support.

Quoted 7 lines
Quoted 3 lines

The only thing stopping me from getting the speed is the fact that struct fw_request is opaque. The value is easily available from request->response.speed and I kind of do that already in a very hackish way. I've sent a separate patch which adds a function that can be used to access that one value.

Waiting until the bus scan is complete isn't actually that great as I see the first LOGIN requests often before the fw_node is seen at all. I'd have to turn away the requester and hope they try again. I'm fairly sure my little tweak in my patch is a simple enough solution.

Stupid question: Could you use a completion queue or something equivalent to wait until you have seen the fw_node, *then* process the LOGIN request?

The fw_address_handler callback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must call fw_send_response() from within the callback and can't defer that until I've scheduled something on a work queue. Please correct me if I'm wrong though, as that might be useful anyway.

Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#14 lore raw
Quoted 44 lines · Chris Boot
Quoted 41 lines · Julian Calaby

Hi,

Quoted 38 lines · Chris Boot
Quoted 28 lines · Stefan Richter
Quoted 10 lines · On Feb 06 Chris Boot wrote:
Quoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

The TODO file says:

Quoted 1 line

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

Quoted 8 lines

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

(T10/1155D Revision 4 page 53/54)

I guess it is not too hard to add this to the AR-req handler. On the other hand, I see little reason to follow the SBP-2 spec to the letter here. The target driver could just use the maximum speed that the core figured out. On the other hand, this requires of course
- the target to wait for core to finish scanning an initiator,
- the core to offer an API to look up an fw_device by a card--generation--nodeID tuple.

Quoted 5 lines

The intention of the spec is IMO clearly to enable target implementations that do not need to implement topology scanning. I have a hard time to think of a valid scenario where an initiator needs to be able to steer a target towards a lower wire speed than what the participating links and PHYs actually support.

The only thing stopping me from getting the speed is the fact that struct fw_request is opaque. The value is easily available from request->response.speed and I kind of do that already in a very hackish way. I've sent a separate patch which adds a function that can be used to access that one value.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

Waiting until the bus scan is complete isn't actually that great as I see the first LOGIN requests often before the fw_node is seen at all. I'd have to turn away the requester and hope they try again. I'm fairly sure my little tweak in my patch is a simple enough solution.

Stupid question: Could you use a completion queue or something equivalent to wait until you have seen the fw_node, *then* process the LOGIN request?

The fw_address_handler callback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must call fw_send_response() from within the callback and can't defer that until I've scheduled something on a work queue. Please correct me if I'm wrong though, as that might be useful anyway.

Hmm sorry I've thought about this overnight and clearly I was talking rubbish. Yes, I need to reply in the fw_address_handler but all I tend to do in there is schedule a task to the the main part of the work anyway. As most of the operations require fetching an ORB from the initiator I have to do this from user context.

So it's possible I could do this by waiting in my scheduled work function until the fw_node is available and get the speed from that - but that seems like an inordinate amount of work when I can follow the standard and do it really easily by pulling it out of the fw_request.

Chris

Signature · 3 lines
Chris Boot
[email protected]
Tel: 01271 414100
#15 lore raw

Hi,

Quoted 88 lines · Chris Boot
Quoted 74 lines · Chris Boot
Quoted 65 lines · Julian Calaby

Hi,

Quoted 56 lines · Chris Boot
Quoted 38 lines · Stefan Richter
Quoted 26 lines · On Feb 06 Chris Boot wrote:
Quoted 7 lines · Clemens Ladisch
Quoted 3 lines · Chris Boot

You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.git

Quoted 7 lines

The TODO file says:

Quoted 3 lines

* Update Juju so we can get the speed in the fw_address_handler callback

What is the speed needed for?

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch
ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

Quoted 1 line

(T10/1155D Revision 4 page 53/54)

I guess it is not too hard to add this to the AR-req handler.  On the other hand, I see little reason to follow the SBP-2 spec to the letter here.  The target driver could just use the maximum speed that the core figured out.  On the other hand, this requires of course
 - the target to wait for core to finish scanning an initiator,
 - the core to offer an API to look up an fw_device by a card--generation--nodeID tuple.

Quoted 7 lines

The intention of the spec is IMO clearly to enable target implementations
that do not need to implement topology scanning.  I have a hard time to think of a valid scenario where an initiator needs to be able to steer a
target towards a lower wire speed than what the participating links and PHYs actually support.

The only thing stopping me from getting the speed is the fact that struct fw_request is opaque. The value is easily available from request->response.speed and I kind of do that already in a very hackish way. I've sent a separate patch which adds a function that can be used to access that one value.

Quoted 4 lines

Waiting until the bus scan is complete isn't actually that great as I see the first LOGIN requests often before the fw_node is seen at all. I'd have to turn away the requester and hope they try again. I'm fairly sure my little tweak in my patch is a simple enough solution.

Quoted 3 lines

Stupid question: Could you use a completion queue or something equivalent to wait until you have seen the fw_node, *then* process the LOGIN request?

Quoted 5 lines

The fw_address_handler callback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must call fw_send_response() from within the callback and can't defer that until I've scheduled something on a work queue. Please correct me if I'm wrong though, as that might be useful anyway.

Hmm sorry I've thought about this overnight and clearly I was talking rubbish. Yes, I need to reply in the fw_address_handler but all I tend to do in there is schedule a task to the the main part of the work anyway. As most of the operations require fetching an ORB from the initiator I have to do this from user context.

Quoted 4 lines

So it's possible I could do this by waiting in my scheduled work function until the fw_node is available and get the speed from that - but that seems like an inordinate amount of work when I can follow the standard and do it really easily by pulling it out of the fw_request.

Fair enough, I assumed that there might have been some reason why you wouldn't have it at that point, not just convention getting in the way.

Thanks,

Signature · 5 lines
Julian Calaby

Email: [email protected]
Profile: http://www.google.com/profiles/julian.calaby/
.Plan: http://sites.google.com/site/juliancalaby/
#16 lore raw
In reply to Chris Boot
Quoted 6 lines · On Feb 07 Chris Boot wrote:
Quoted 5 lines · Chris Boot
Quoted 4 lines · Julian Calaby
Quoted 3 lines · Chris Boot

Waiting until the bus scan is complete isn't actually that great as I see the first LOGIN requests often before the fw_node is seen at all. I'd have to turn away the requester and hope they try again.

An SBP-2 initiator should be prepared to retry its first login attempt if it sent it shortly after a bus reset. The target may still hold reservations for previously loggend in initiators for up to reconnect_hold
+ 2 seconds after bus reset.

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

I'm fairly sure my little tweak in my patch is a simple enough solution.

Yep.

Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

Stupid question: Could you use a completion queue or something equivalent to wait until you have seen the fw_node, *then* process the LOGIN request?

The fw_address_handler callback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must call fw_send_response() from within the callback and can't defer that until I've scheduled something on a work queue. Please correct me if I'm wrong though, as that might be useful anyway.

Quoted 5 lines

Hmm sorry I've thought about this overnight and clearly I was talking rubbish. Yes, I need to reply in the fw_address_handler but all I tend to do in there is schedule a task to the the main part of the work anyway. As most of the operations require fetching an ORB from the initiator I have to do this from user context.

Technically there are two things to perform:

1. Finish the inbound IEEE 1394 transaction to the management agent register by means of fw_send_response(). As far as I can tell, you don't have to do that in the address_callback(). But there is little reason not to.

fw_send_response() ends the lifetime of an fw_request, so read the speed code before you respond.

2. Finish the inbound SBP-2 transaction; here the login. This and everything that leads up to it is definitely easiest to implement in a process context, e.g. workqueue item.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- --===
http://arcgraph.de/sr/
author maintainer
#17 lore raw
Quoted 26 lines · Stefan Richter
Quoted 12 lines · On Feb 07 Chris Boot wrote:
Quoted 10 lines · Chris Boot
Quoted 3 lines · Julian Calaby

Stupid question: Could you use a completion queue or something equivalent to wait until you have seen the fw_node, *then* process the LOGIN request?

The fw_address_handler callback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must call fw_send_response() from within the callback and can't defer that until I've scheduled something on a work queue. Please correct me if I'm wrong though, as that might be useful anyway.

Quoted 5 lines

Hmm sorry I've thought about this overnight and clearly I was talking rubbish. Yes, I need to reply in the fw_address_handler but all I tend to do in there is schedule a task to the the main part of the work anyway. As most of the operations require fetching an ORB from the initiator I have to do this from user context.

Technically there are two things to perform:

1. Finish the inbound IEEE 1394 transaction to the management agent register by means of fw_send_response(). As far as I can tell, you don't have to do that in the address_callback(). But there is little reason not to.

Quoted 6 lines

fw_send_response() ends the lifetime of an fw_request, so read the speed code before you respond.

2. Finish the inbound SBP-2 transaction; here the login. This and everything that leads up to it is definitely easiest to implement in a process context, e.g. workqueue item.

Yep I do exactly that - I save the speed then schedule_work() inside the address callback, then call fw_send_response() still within the callback. The work callback uses fw_run_transaction() to fetch the ORB and deal with it.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#18 lore raw

cover firewire-sbp-target: FireWire SBP-2 SCSI target

In reply to Chris Boot

Hi folks,

Well after lots of work I have a working and generally (at least I think) sensible starting point for the FireWire target. It appears to work fine in all the configurations I've tested it against, including Linux and Mac OS X initiators.

This is not production ready code, and not ready for merging IMO. I know my locking (or more the lack of it) leaves something to be desired, and I know my use of atomics and memory barriers is wrong too. Performance is also a bit meagre and I'm sure it can be improved substantially - I already have some ideas for things that can be improved.

For testing, I use:

GUID=5254000b8f01e6f6
DIR_ID=abcdef

modprobe firewire-sbp-target
mkdir /sys/kernel/config/target/sbp
mkdir /sys/kernel/config/target/sbp/$GUID
mkdir /sys/kernel/config/target/sbp/$GUID/tpgt_1
mkdir /sys/kernel/config/target/sbp/$GUID/tpgt_1/lun/lun_0
ln -s /sys/kernel/config/target/core/iblock_0/sbptest /sys/kernel/config/target/sbp/$GUID/tpgt_1/lun/lun_0/sbptest echo $DIR_ID > /sys/kernel/config/target/sbp/$GUID/tpgt_1/directory_id
echo 1 > /sys/kernel/config/target/sbp/$GUID/tpgt_1/enable

Setting the directory_id is optional and something I have only just added, but it appears to work as planned.

Any and all comments much appreciated.

Thanks,
Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#19 lore raw

PATCH 1/13 firewire: Add function to get speed from opaque struct fw_request

Sometimes it's useful to know the FireWire speed of the request that has just come in to a fw_address_handler callback. As struct fw_request is opaque we can't peek inside to get the speed out of the struct fw_packet that's just inside. For example, the SBP-2 spec says:

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

[ ANSI T10/1155D Revision 4 page 53/54 ]

---
 drivers/firewire/core-transaction.c |   16 ++++++++++++++++
 include/linux/firewire.h            |    1 +
 2 files changed, 17 insertions(+), 0 deletions(-)
drivers/firewire/core-transaction.c +16 -0
index 855ab3f..614f592 100644
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
 }
 EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request:	The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
 static void handle_exclusive_region_request(struct fw_card *card,
 					    struct fw_packet *p,
 					    struct fw_request *request,
include/linux/firewire.h +1 -0
index 84ccf8e..f010307 100644
@@ -340,6 +340,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
 void fw_core_remove_address_handler(struct fw_address_handler *handler);
 void fw_send_response(struct fw_card *card,
 		      struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
 void fw_send_request(struct fw_card *card, struct fw_transaction *t,
 		     int tcode, int destination_id, int generation, int speed,
 		     unsigned long long offset, void *payload, size_t length,
Signature · 1 line
1.7.9
author maintainer
#20 lore raw

PATCH 2/13 firewire: Add EXPORT_SYMBOL_GPL(fw_card_release)

In reply to Chris Boot

The firewire-sbp-target module requires this so it can keep a reference to the fw_card object in order that it can fetch ORBs to execute and read/write related data and status information.

Cc Clemens Ladisch <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/firewire/core-card.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
drivers/firewire/core-card.c +1 -0
index 85661b0..42b180b 100644
@@ -654,6 +654,7 @@ void fw_card_release(struct kref *kref)
 
 	complete(&card->done);
 }
+EXPORT_SYMBOL_GPL(fw_card_release);
 
 void fw_core_remove_card(struct fw_card *card)
 {
Signature · 1 line
1.7.9
author maintainer
#21 lore raw

PATCH 3/13 firewire-sbp-target: Add Kconfig, Makefile and TODO

In reply to Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on older Apple computers.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/Kconfig  |   14 ++++++++++++++
 drivers/target/sbp/Makefile |   13 +++++++++++++
 drivers/target/sbp/TODO     |    7 +++++++
 3 files changed, 34 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/Kconfig
 create mode 100644 drivers/target/sbp/Makefile
 create mode 100644 drivers/target/sbp/TODO
drivers/target/sbp/Kconfig new file +14 -0
new file mode 100644
index 0000000..8544010
@@ -0,0 +1,14 @@
+
+config FIREWIRE_SBP_TARGET
+	tristate "FireWire SBP-2 fabric module"
+	depends on TARGET_CORE && CONFIGFS_FS && FIREWIRE && EXPERIMENTAL
+	default n
+	help
+	  Say Y or M here to enable SCSI target functionality over FireWire.
+	  This enables you to expose SCSI devices to other nodes on the FireWire
+	  bus, for example hard disks. Similar to FireWire Target Disk mode on
+	  older Apple computers.
+
+	  To compile this driver as a module, say M here: The module will be
+	  called firewire-sbp-target.
+
drivers/target/sbp/Makefile new file +13 -0
new file mode 100644
index 0000000..3a3e155
@@ -0,0 +1,13 @@
+
+firewire-sbp-target-y += \
+	sbp_configfs.o \
+	sbp_fabric.o \
+	sbp_login.o \
+	sbp_management_agent.o \
+	sbp_proto.o \
+	sbp_scsi_cmnd.o \
+	sbp_target_agent.o \
+	sbp_util.o
+
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += firewire-sbp-target.o
+
drivers/target/sbp/TODO new file +7 -0
new file mode 100644
index 0000000..eaec1c9
@@ -0,0 +1,7 @@
+* Force-terminate sessions when disabling targets
+* Ability to have several SCSI commands in-flight (TCQ?)
+* Retry failed FireWire transactions a few times with exponential backoff
+* Take into account the page_size field for transfers and/or page tables
+* Handle descriptor format sense data
+* Implement QUERY LOGINS management ORB
+* Implement TASK MANAGEMENT functionality
Signature · 1 line
1.7.9
#22 lore raw

Re: [PATCH 03/13] firewire-sbp-target: Add Kconfig, Makefile and TODO

Quoted 30 lines · Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on older Apple computers.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/Kconfig  |   14 ++++++++++++++
 drivers/target/sbp/Makefile |   13 +++++++++++++
 drivers/target/sbp/TODO     |    7 +++++++
 3 files changed, 34 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/Kconfig
 create mode 100644 drivers/target/sbp/Makefile
 create mode 100644 drivers/target/sbp/TODO
drivers/target/sbp/Kconfig new file +4 -0
new file mode 100644
index 0000000..8544010
@@ -0,0 +1,14 @@
+
+config FIREWIRE_SBP_TARGET
+	tristate "FireWire SBP-2 fabric module"
+	depends on TARGET_CORE && CONFIGFS_FS && FIREWIRE && EXPERIMENTAL

drivers/target/sbp/Kconfig line 14

Quoted 10 lines
drivers/target/sbp/Kconfig new file +10 -0
+	default n
+	help
+	  Say Y or M here to enable SCSI target functionality over FireWire.
+	  This enables you to expose SCSI devices to other nodes on the FireWire
+	  bus, for example hard disks. Similar to FireWire Target Disk mode on
+	  older Apple computers.
+
+	  To compile this driver as a module, say M here: The module will be
+	  called firewire-sbp-target.
+

Hi Chris,

Just a minor Kconfig related item here. You can go ahead and drop the extra depends on TARGET_CORE && CONFIGFS_FS here, as drivers/target/Kconfig is already handling the dependencies for these two config options before including drivers/target/sbp/Kconfig.

Thanks!

--nab

author maintainer
#23 lore raw

PATCH 4/13 firewire-sbp-target: Add sbp_base.h header

In reply to Chris Boot

This header contains defines and structures that are common to many of the modules of the target code. This includes SBP-2 protocol structures and constants as well as a few structs for setting up the target, LUN login information and session setup.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_base.h |  196 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 196 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_base.h
drivers/target/sbp/sbp_base.h new file +196 -0
new file mode 100644
index 0000000..629ae1a
@@ -0,0 +1,196 @@
+
+#define SBP_VERSION  "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE	8
+
+#define MANAGEMENT_AGENT_STATE_IDLE	0
+#define MANAGEMENT_AGENT_STATE_BUSY	1
+
+#define ORB_NOTIFY(v)			(((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v)		(((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v)	(((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN			0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS		0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT		0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD		0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT			0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK		0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET		0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET	0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET		0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v)		(((v) >> 28) &   0x01)
+#define LOGIN_ORB_RESERVED(v)		(((v) >> 24) &   0x0f)
+#define LOGIN_ORB_RECONNECT(v)		(((v) >> 20) &   0x0f)
+#define LOGIN_ORB_LUN(v)		(((v) >>  0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v)	(((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v)	(((v) >>  0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v)	(((v) >>  0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v)		(((v) >>  0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v)		(((v) >> 27) &   0x01)
+#define CMDBLK_ORB_SPEED(v)		(((v) >> 24) &   0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v)	(((v) >> 20) &   0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v)	(((v) >> 19) &   0x01)
+#define CMDBLK_ORB_PG_SIZE(v)		(((v) >> 16) &   0x07)
+#define CMDBLK_ORB_DATA_SIZE(v)		(((v) >>  0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v)		(((v) &   0x03) << 30)
+#define STATUS_BLOCK_RESP(v)		(((v) &   0x03) << 28)
+#define STATUS_BLOCK_DEAD(v)		(((v) ? 1 : 0)  << 27)
+#define STATUS_BLOCK_LEN(v)		(((v) &   0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v)	(((v) &   0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v)	(((v) & 0xffff) <<  0)
+
+#define STATUS_SRC_ORB_CONTINUING	0
+#define STATUS_SRC_ORB_FINISHED		1
+#define STATUS_SRC_UNSOLICITED		2
+
+#define STATUS_RESP_REQUEST_COMPLETE	0
+#define STATUS_RESP_TRANSPORT_FAILURE	1
+#define STATUS_RESP_ILLEGAL_REQUEST	2
+#define STATUS_RESP_VENDOR_DEPENDENT	3
+
+#define SBP_STATUS_OK			0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP	1
+#define SBP_STATUS_SPEED_NOTSUPP	2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP	3
+#define SBP_STATUS_ACCESS_DENIED	4
+#define SBP_STATUS_LUN_NOTSUPP		5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL	6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL	8
+#define SBP_STATUS_FUNCTION_REJECTED	9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN	10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE	11
+#define SBP_STATUS_REQUEST_ABORTED	12
+#define SBP_STATUS_UNSPECIFIED_ERROR	0xff
+
+#define AGENT_STATE_RESET	0
+#define AGENT_STATE_ACTIVE	1
+#define AGENT_STATE_SUSPENDED	2
+#define AGENT_STATE_DEAD	3
+
+struct sbp2_pointer {
+	__be32 high;
+	__be32 low;
+};
+
+struct sbp_command_block_orb {
+	struct sbp2_pointer next_orb;
+	struct sbp2_pointer data_descriptor;
+	__be32 misc;
+	u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+	__be16 segment_length;
+	__be16 segment_base_hi;
+	__be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+	struct sbp2_pointer ptr1;
+	struct sbp2_pointer ptr2;
+	__be32 misc;
+	__be32 length;
+	struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+	__be32 status;
+	__be32 orb_low;
+	u8 data[24];
+};
+
+struct sbp_login_response_block {
+	__be32 misc;
+	struct sbp2_pointer command_block_agent;
+	__be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+	struct sbp_session *sess;
+	struct list_head link;
+
+	struct se_lun *lun;
+
+	u64 status_fifo_addr;
+	int exclusive;
+	u16 login_id;
+	atomic_t unsolicited_status_enable;
+
+	struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+	struct se_session *se_sess;
+	struct list_head login_list;
+	struct delayed_work maint_work;
+
+	u64 guid; /* login_owner_EUI_64 */
+	int node_id; /* login_owner_ID */
+
+	struct fw_card *card;
+	int generation;
+	int speed;
+
+	int reconnect_hold;
+	u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+	/* Initiator EUI-64 */
+	u64 guid;
+	/* ASCII formatted GUID for SBP Initiator port */
+	char iport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_nodeacl() */
+	struct se_node_acl se_node_acl;
+};
+
+struct sbp_lun {
+	struct list_head link;
+	struct se_lun *se_lun;
+};
+
+struct sbp_tpg {
+	/* Target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer back to sbp_tport */
+	struct sbp_tport *tport;
+	/* Returned by sbp_make_tpg() */
+	struct se_portal_group se_tpg;
+
+	struct list_head lun_list;
+};
+
+struct sbp_tport {
+	/* Target Unit Identifier (EUI-64) */
+	u64 guid;
+	/* Target port name */
+	char tport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_tport() */
+	struct se_wwn tport_wwn;
+
+	struct sbp_tpg *tpg;
+
+	/* FireWire unit directory */
+	struct fw_descriptor unit_directory;
+
+	/* SBP Management Agent */
+	struct sbp_management_agent *mgt_agt;
+
+	/* Parameters */
+	int enable;
+	s32 directory_id;
+	int mgt_orb_timeout;
+	int max_reconnect_timeout;
+	int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+
Signature · 1 line
1.7.9
author maintainer
#24 lore raw

PATCH 5/13 firewire-sbp-target: Add sbp_configfs.c

In reply to Chris Boot

This is used to glue the target framework's configfs code to the target code, and what is used to create targets and link them to LUNs to export. The code to create the FireWire unit directory to advertise targets on the FireWire bus is also in here.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_configfs.c |  759 +++++++++++++++++++++++++++++++++++++
 1 files changed, 759 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_configfs.c
drivers/target/sbp/sbp_configfs.c new file +759 -0
new file mode 100644
index 0000000..530d1d7
@@ -0,0 +1,759 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+#include "sbp_proto.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+static const u32 sbp_unit_directory_template[] = {
+	0x1200609e, /* unit_specifier_id: NCITS/T10 */
+	0x13010483, /* unit_sw_version: 1155D Rev 4 */
+	0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+	0x390104d8, /* command_set: SPC-2 */
+	0x3b000000, /* command_set_revision: 0 */
+	0x3c000001, /* firmware_revision: 1 */
+};
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+	struct sbp_lun *lun;
+	int num_luns = 0, num_entries, idx = 0, mgt_agt_addr, ret;
+	u32 *data;
+
+	/* unregister existing descriptor */
+	if (tport->unit_directory.data) {
+		fw_core_remove_descriptor(&tport->unit_directory);
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	if (!tport->enable || !tport->tpg)
+		return 0;
+
+	/* count how many LUNs to publish */
+	list_for_each_entry(lun, &tport->tpg->lun_list, link)
+		num_luns++;
+
+	/*
+	 * Number of entries in the final unit directory:
+	 *  - all of those in the template
+	 *  - management_agent
+	 *  - unit_characteristics
+	 *  - reconnect_timeout
+	 *  - unit unique ID
+	 *  - one for each LUN
+	 *
+	 *  MUST NOT include leaf or sub-directory entries
+	 */
+	num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+	if (tport->directory_id != -1)
+		num_entries++;
+
+	/* allocate num_entries + 4 for the header and unique ID leaf */
+	data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* directory_length */
+	data[idx++] = num_entries << 16;
+
+	/* directory_id */
+	if (tport->directory_id != -1)
+		data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+	/* directory template */
+	memcpy(&data[idx], sbp_unit_directory_template,
+			sizeof(sbp_unit_directory_template));
+	idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+	/* management_agent */
+	mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+	data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+	/* unit_characteristics */
+	data[idx++] = 0x3a000000 |
+		(((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+		SBP_ORB_FETCH_SIZE;
+
+	/* reconnect_timeout */
+	data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+	/* unit unique ID (leaf is just after LUNs) */
+	data[idx++] = 0x8d000000 | (num_luns + 1);
+
+	/* LUNs */
+	list_for_each_entry(lun, &tport->tpg->lun_list, link) {
+		struct se_lun *se_lun = lun->se_lun;
+		struct se_device *dev = se_lun->lun_se_dev;
+		int type = dev->transport->get_device_type(dev);
+
+		/* logical_unit_number */
+		data[idx++] = 0x14000000 |
+			((type << 16) & 0x1f0000) |
+			(se_lun->unpacked_lun & 0xffff);
+	}
+
+	/* unit unique ID leaf */
+	data[idx++] = 2 << 16;
+	data[idx++] = tport->guid >> 32;
+	data[idx++] = tport->guid;
+
+	tport->unit_directory.length = idx;
+	tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+	tport->unit_directory.data = data;
+
+	ret = fw_core_add_descriptor(&tport->unit_directory);
+	if (ret < 0) {
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+	const char *cp;
+	char c, nibble;
+	int pos = 0, err;
+
+	*wwn = 0;
+	for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+		c = *cp;
+		if (c == '\n' && cp[1] == '\0')
+			continue;
+		if (c == '\0') {
+			err = 2;
+			if (pos != 16)
+				goto fail;
+			return cp - name;
+		}
+		err = 3;
+		if (isdigit(c))
+			nibble = c - '0';
+		else if (isxdigit(c) && (islower(c) || !strict))
+			nibble = tolower(c) - 'a' + 10;
+		else
+			goto fail;
+		*wwn = (*wwn << 4) | nibble;
+		pos++;
+	}
+	err = 4;
+fail:
+	printk(KERN_INFO "err %u len %zu pos %u\n",
+			err, cp - name, pos);
+	return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+	return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+		struct se_portal_group *se_tpg,
+		struct config_group *group,
+		const char *name)
+{
+	struct se_node_acl *se_nacl, *se_nacl_new;
+	struct sbp_nacl *nacl;
+	u64 guid = 0;
+	u32 nexus_depth = 1;
+
+	pr_info("sbp_make_nodeacl: %s\n", name);
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+	if (!se_nacl_new)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+	 * when converting a NodeACL from demo mode -> explict
+	 */
+	se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+			name, nexus_depth);
+	if (IS_ERR(se_nacl)) {
+		sbp_release_fabric_acl(se_tpg, se_nacl_new);
+		return se_nacl;
+	}
+
+	/*
+	 * Locate our struct sbp_nacl and set the FC Nport WWPN
+	 */
+	nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	nacl->guid = guid;
+	sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+	return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+	core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+	kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_lun *lun;
+
+	pr_info("sbp_post_link_lun: LUN %d\n", se_lun->unpacked_lun);
+
+	lun = kmalloc(sizeof(*lun), GFP_KERNEL);
+	if (!lun)
+		return -ENOMEM;
+
+	lun->se_lun = se_lun;
+	list_add_tail(&lun->link, &tpg->lun_list);
+
+	return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	struct sbp_lun *lun;
+	int ret;
+
+	pr_info("sbp_pre_unlink_lun: LUN %d\n", se_lun->unpacked_lun);
+
+	list_for_each_entry(lun, &tpg->lun_list, link) {
+		if (lun->se_lun == se_lun)
+			break;
+	}
+
+	BUG_ON(lun == NULL);
+
+	list_del(&lun->link);
+	kfree(lun);
+
+	if (list_empty(&tpg->lun_list))
+		tport->enable = 0;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+		struct se_wwn *wwn,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	struct sbp_tpg *tpg;
+	unsigned long tpgt;
+	int ret;
+
+	if (strstr(name, "tpgt_") != name)
+		return ERR_PTR(-EINVAL);
+	if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+		return ERR_PTR(-EINVAL);
+
+	if (tport->tpg) {
+		pr_err("Only one TPG per Unit is possible.\n");
+		return ERR_PTR(-EBUSY);
+	}
+
+	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+	if (!tpg) {
+		pr_err("Unable to allocate struct sbp_tpg\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tpg->tport = tport;
+	tpg->tport_tpgt = tpgt;
+	tport->tpg = tpg;
+
+	/* default attribute values */
+	tport->enable = 0;
+	tport->directory_id = -1;
+	tport->mgt_orb_timeout = 15;
+	tport->max_reconnect_timeout = 5;
+	tport->max_logins_per_lun = 1;
+
+	INIT_LIST_HEAD(&tpg->lun_list);
+
+	tport->mgt_agt = sbp_management_agent_register(tport);
+	if (IS_ERR(tport->mgt_agt)) {
+		ret = PTR_ERR(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+			&tpg->se_tpg, (void *)tpg,
+			TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret < 0) {
+		sbp_management_agent_unregister(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	core_tpg_deregister(se_tpg);
+	sbp_management_agent_unregister(tport->mgt_agt);
+	tport->tpg = NULL;
+	kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+		struct target_fabric_configfs *tf,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport;
+	u64 guid = 0;
+
+	pr_info("sbp_make_tport: %s\n", name);
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+	if (!tport) {
+		pr_err("Unable to allocate struct sbp_tport\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tport->guid = guid;
+	sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+	return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+		struct target_fabric_configfs *tf,
+		char *page)
+{
+	return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+	&sbp_wwn_version.attr,
+	NULL,
+};
+
+
+static ssize_t sbp_tpg_show_directory_id(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	if (tport->directory_id == -1)
+		return sprintf(page, "implicit\n");
+	else
+		return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (tport->enable) {
+		pr_err("Cannot change the directory_id on an active target.\n");
+		return -EBUSY;
+	}
+
+	if (strstr(page, "implicit") == page) {
+		tport->directory_id = -1;
+	} else {
+		if (kstrtoul(page, 16, &val) < 0)
+			return -EINVAL;
+		if (val > 0xffffff)
+			return -EINVAL;
+
+		tport->directory_id = val;
+	}
+
+	return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val != 0) && (val != 1))
+		return -EINVAL;
+
+	if (tport->enable == val)
+		return count;
+
+	if (val) {
+		if (list_empty(&tpg->lun_list)) {
+			pr_err("Cannot enable a target with no LUNs!\n");
+			return -EINVAL;
+		}
+	} else {
+		/* FIXME: force-shutdown sessions instead */
+		if (!list_empty(&se_tpg->tpg_sess_list))
+			return -EBUSY;
+	}
+
+	tport->enable = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0) {
+		pr_err("Could not update Config ROM\n");
+		return ret;
+	}
+
+	return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+	&sbp_tpg_directory_id.attr,
+	&sbp_tpg_enable.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	if (tport->mgt_orb_timeout == val)
+		return count;
+
+	tport->mgt_orb_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 32767))
+		return -EINVAL;
+
+	if (tport->max_reconnect_timeout == val)
+		return count;
+
+	tport->max_reconnect_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	/* XXX: also check against current count? */
+
+	tport->max_logins_per_lun = val;
+
+	return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+	&sbp_tpg_attrib_mgt_orb_timeout.attr,
+	&sbp_tpg_attrib_max_reconnect_timeout.attr,
+	&sbp_tpg_attrib_max_logins_per_lun.attr,
+	NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+	.get_fabric_name		= sbp_get_fabric_name,
+	.get_fabric_proto_ident		= sbp_get_fabric_proto_ident,
+	.tpg_get_wwn			= sbp_get_fabric_wwn,
+	.tpg_get_tag			= sbp_get_tag,
+	.tpg_get_default_depth		= sbp_get_default_depth,
+	.tpg_get_pr_transport_id	= sbp_get_pr_transport_id,
+	.tpg_get_pr_transport_id_len	= sbp_get_pr_transport_id_len,
+	.tpg_parse_pr_out_transport_id	= sbp_parse_pr_out_transport_id,
+	.tpg_check_demo_mode		= sbp_check_true,
+	.tpg_check_demo_mode_cache	= sbp_check_true,
+	.tpg_check_demo_mode_write_protect = sbp_check_false,
+	.tpg_check_prod_mode_write_protect = sbp_check_false,
+	.tpg_alloc_fabric_acl		= sbp_alloc_fabric_acl,
+	.tpg_release_fabric_acl		= sbp_release_fabric_acl,
+	.tpg_get_inst_index		= sbp_tpg_get_inst_index,
+	.new_cmd_map			= sbp_new_cmd,
+	.release_cmd			= sbp_release_cmd,
+	.shutdown_session		= sbp_shutdown_session,
+	.close_session			= sbp_close_session,
+	.stop_session			= sbp_stop_session,
+	.fall_back_to_erl0		= sbp_reset_nexus,
+	.sess_logged_in			= sbp_sess_logged_in,
+	.sess_get_index			= sbp_sess_get_index,
+	.sess_get_initiator_sid		= NULL,
+	.write_pending			= sbp_write_pending,
+	.write_pending_status		= sbp_write_pending_status,
+	.set_default_node_attributes	= sbp_set_default_node_attrs,
+	.get_task_tag			= sbp_get_task_tag,
+	.get_cmd_state			= sbp_get_cmd_state,
+	.queue_data_in			= sbp_queue_data_in,
+	.queue_status			= sbp_queue_status,
+	.queue_tm_rsp			= sbp_queue_tm_rsp,
+	.get_fabric_sense_len		= sbp_get_fabric_sense_len,
+	.set_fabric_sense_len		= sbp_set_fabric_sense_len,
+	.is_state_remove		= sbp_is_state_remove,
+	.check_stop_free		= sbp_check_stop_free,
+
+	.fabric_make_wwn		= sbp_make_tport,
+	.fabric_drop_wwn		= sbp_drop_tport,
+	.fabric_make_tpg		= sbp_make_tpg,
+	.fabric_drop_tpg		= sbp_drop_tpg,
+	.fabric_post_link		= sbp_post_link_lun,
+	.fabric_pre_unlink		= sbp_pre_unlink_lun,
+	.fabric_make_np			= NULL,
+	.fabric_drop_np			= NULL,
+	.fabric_make_nodeacl		= sbp_make_nodeacl,
+	.fabric_drop_nodeacl		= sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	pr_info("FireWire SBP fabric module %s\n", SBP_VERSION);
+
+	/*
+	 * Register the top level struct config_item_type with TCM core
+	 */
+	fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+	if (!fabric) {
+		pr_err("target_fabric_configfs_init() failed\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Setup fabric->tf_ops from our local sbp_ops
+	 */
+	fabric->tf_ops = sbp_ops;
+
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 */
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+	/*
+	 * Register the fabric for use within TCM
+	 */
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		pr_err("target_fabric_configfs_register() failed for SBP\n");
+		return ret;
+	}
+
+	/*
+	 * Setup our local pointer to *fabric
+	 */
+	sbp_fabric_configfs = fabric;
+
+	pr_info("SBP[0] - Set fabric -> sbp_fabric_configfs\n");
+	return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+	if (!sbp_fabric_configfs)
+		return;
+
+	target_fabric_configfs_deregister(sbp_fabric_configfs);
+	sbp_fabric_configfs = NULL;
+	pr_info("SBP[0] - Cleared sbp_fabric_configfs\n");
+};
+
+static int __init sbp_init(void)
+{
+	int ret;
+
+	ret = sbp_register_configfs();
+	if (ret < 0)
+		return ret;
+
+	return 0;
+};
+
+static void sbp_exit(void)
+{
+	sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
+
Signature · 1 line
1.7.9
author maintainer
#25 lore raw

PATCH 6/13 firewire-sbp-target: Add sbp_fabric.{c,h}

In reply to Chris Boot

This serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_fabric.c |  321 +++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_fabric.h |   32 ++++
 2 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_fabric.c
 create mode 100644 drivers/target/sbp/sbp_fabric.h
drivers/target/sbp/sbp_fabric.c new file +321 -0
new file mode 100644
index 0000000..edc6fda
@@ -0,0 +1,321 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+int sbp_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+int sbp_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+char *sbp_get_fabric_name(void)
+{
+	return "sbp";
+}
+
+char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	return &tport->tport_name[0];
+}
+
+u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	return tpg->tport_tpgt;
+}
+
+u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+	struct sbp_nacl *nacl;
+
+	nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+	if (!nacl) {
+		pr_err("Unable to alocate struct sbp_nacl\n");
+		return NULL;
+	}
+
+	return &nacl->se_node_acl;
+}
+
+void sbp_release_fabric_acl(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	kfree(nacl);
+}
+
+u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+int sbp_new_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = transport_generic_allocate_tasks(se_cmd, req->cmd_buf);
+	if (ret)
+		return ret;
+
+	return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, NULL, 0);
+}
+
+void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	sbp_free_request(req);
+}
+
+int sbp_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+void sbp_close_session(struct se_session *se_sess)
+{
+	return;
+}
+
+void sbp_stop_session(struct se_session *se_sess, int sess_sleep,
+		int conn_sleep)
+{
+	return;
+}
+
+void sbp_reset_nexus(struct se_session *se_sess)
+{
+	return;
+}
+
+int sbp_sess_logged_in(struct se_session *se_sess)
+{
+	return 0;
+}
+
+u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+int sbp_write_pending(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	if (!req->data_len)
+		return -EINVAL;
+
+	if (req->data_dir != DMA_TO_DEVICE) {
+		pr_err("sbp_write_pending: incorrect data direction\n");
+		return -EINVAL;
+	}
+
+	if (req->data_len != se_cmd->data_length) {
+		pr_warn("sbp_write_pending: dodgy data length (%d != %d)\n",
+			req->data_len, se_cmd->data_length);
+	}
+
+	req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+	if (!req->data_buf)
+		return -ENOMEM;
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		pr_warn("sbp_write_pending: data write error\n");
+		return ret;
+	}
+
+	sg_copy_from_buffer(se_cmd->t_data_sg,
+			se_cmd->t_data_nents,
+			req->data_buf,
+			se_cmd->data_length);
+
+	transport_generic_process_write(se_cmd);
+
+	return 0;
+}
+
+int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+	return;
+}
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	/* only used for printk and family? */
+	return (u32)req->orb_pointer;
+}
+
+int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	if (!req->data_len) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_ILLEGAL_REQUEST) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		pr_err("sbp_queue_data_in: no initiator data buffers\n");
+		return 0;
+	}
+
+	if (req->data_dir != DMA_FROM_DEVICE) {
+		pr_err("sbp_queue_data_in: incorrect data direction\n");
+		return -EINVAL;
+	}
+
+	if (req->data_len != se_cmd->data_length) {
+		pr_warn("sbp_write_pending: dodgy data length (%d != %d)\n",
+			req->data_len, se_cmd->data_length);
+	}
+
+	req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+	if (!req->data_buf)
+		return -ENOMEM;
+
+	sg_copy_to_buffer(se_cmd->t_data_sg,
+		se_cmd->t_data_nents,
+		req->data_buf,
+		se_cmd->data_length);
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	return sbp_send_sense(req);
+}
+
+int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+	return 0;
+}
+
+u16 sbp_get_fabric_sense_len(void)
+{
+	return 0;
+}
+
+int sbp_is_state_remove(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	transport_generic_free_cmd(&req->se_cmd, 0);
+	return 1;
+}
+
drivers/target/sbp/sbp_fabric.h new file +32 -0
new file mode 100644
index 0000000..2a600f1c
@@ -0,0 +1,32 @@
+
+int sbp_check_true(struct se_portal_group *);
+int sbp_check_false(struct se_portal_group *);
+char *sbp_get_fabric_name(void);
+char *sbp_get_fabric_wwn(struct se_portal_group *);
+u16 sbp_get_tag(struct se_portal_group *);
+u32 sbp_get_default_depth(struct se_portal_group *);
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *);
+void sbp_release_fabric_acl(struct se_portal_group *,
+		struct se_node_acl *);
+u32 sbp_tpg_get_inst_index(struct se_portal_group *);
+int sbp_new_cmd(struct se_cmd *se_cmd);
+void sbp_release_cmd(struct se_cmd *se_cmd);
+int sbp_shutdown_session(struct se_session *);
+void sbp_close_session(struct se_session *);
+void sbp_stop_session(struct se_session *, int, int);
+void sbp_reset_nexus(struct se_session *);
+int sbp_sess_logged_in(struct se_session *);
+u32 sbp_sess_get_index(struct se_session *);
+int sbp_write_pending(struct se_cmd *);
+int sbp_write_pending_status(struct se_cmd *);
+void sbp_set_default_node_attrs(struct se_node_acl *);
+u32 sbp_get_task_tag(struct se_cmd *);
+int sbp_get_cmd_state(struct se_cmd *);
+int sbp_queue_data_in(struct se_cmd *);
+int sbp_queue_status(struct se_cmd *);
+int sbp_queue_tm_rsp(struct se_cmd *);
+u16 sbp_set_fabric_sense_len(struct se_cmd *, u32);
+u16 sbp_get_fabric_sense_len(void);
+int sbp_is_state_remove(struct se_cmd *);
+int sbp_check_stop_free(struct se_cmd *se_cmd);
+
Signature · 1 line
1.7.9
#26 lore raw

Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

Quoted 41 lines · Chris Boot

This serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_fabric.c |  321 +++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_fabric.h |   32 ++++
 2 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_fabric.c
 create mode 100644 drivers/target/sbp/sbp_fabric.h
drivers/target/sbp/sbp_fabric.c new file +20 -0
new file mode 100644
index 0000000..edc6fda
@@ -0,0 +1,321 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+

<SNIP>

drivers/target/sbp/sbp_fabric.c line 122

Quoted 13 lines
drivers/target/sbp/sbp_fabric.c new file +13 -0
+int sbp_new_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = transport_generic_allocate_tasks(se_cmd, req->cmd_buf);
+	if (ret)
+		return ret;
+
+	return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, NULL, 0);
+}
+

Because sbp_scsi_cmnd.c:sbp_handle_command() is using the new target_submit_cmd() logic, target-core will not be calling TFO->new_cmd_map() -> sbp_new_cmd() for setup here.

Go ahead and drop this now.

drivers/target/sbp/sbp_fabric.c line 225

Quoted 10 lines
drivers/target/sbp/sbp_fabric.c new file +10 -0
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	/* only used for printk and family? */
+	return (u32)req->orb_pointer;
+}
+

So an the ABORT_TASK TMR patches use TFO->get_task_tag() to locate a referenced tag to locate the se_cmd descriptor. Since we don't support TMRs yet in sbp, this value will only be used for informational purposes.

Quoted 53 lines
drivers/target/sbp/sbp_fabric.c new file +53 -0
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	if (!req->data_len) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_ILLEGAL_REQUEST) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		pr_err("sbp_queue_data_in: no initiator data buffers\n");
+		return 0;
+	}
+
+	if (req->data_dir != DMA_FROM_DEVICE) {
+		pr_err("sbp_queue_data_in: incorrect data direction\n");
+		return -EINVAL;
+	}
+
+	if (req->data_len != se_cmd->data_length) {
+		pr_warn("sbp_write_pending: dodgy data length (%d != %d)\n",
+			req->data_len, se_cmd->data_length);
+	}
+
+	req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+	if (!req->data_buf)
+		return -ENOMEM;
+
+	sg_copy_to_buffer(se_cmd->t_data_sg,
+		se_cmd->t_data_nents,
+		req->data_buf,
+		se_cmd->data_length);
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)

drivers/target/sbp/sbp_fabric.c line 292

Quoted 10 lines
drivers/target/sbp/sbp_fabric.c new file +10 -0
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	return sbp_send_sense(req);
+}
+

Thanks!

--nab

A message that is not in the archive (sent off-list or not yet received): [email protected]

#28 lore raw

Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

Quoted 6 lines · On Feb 13 Chris Boot wrote:

I'd like to support ABORT_TASK and friends at some point. The problem I see here though is that tags in SBP-2/3 are 48-bits wide (the ORB address in the initiator node's memory), and the target framework seems to use a u32 for this. SAM-3 Section 4.11 seems to say "A task tag is a value that is composed of up to 64 bits" so it might be worth getting that changed in the target framework?

In practice, its is quite likely that only 32 bit wide SBP tags are being used. But there is no way to guarantee this.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -==-=
http://arcgraph.de/sr/
#29 lore raw

Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}

In reply to a message not in the archive
Quoted 44 lines · Chris Boot
Quoted 21 lines · Nicholas A. Bellinger
Quoted 20 lines · Chris Boot

This serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>

---
drivers/target/sbp/sbp_fabric.c | 321 +++++++++++++++++++++++++++++++++++++++
drivers/target/sbp/sbp_fabric.h | 32 ++++

2 files changed, 353 insertions(+), 0 deletions(-)

create mode 100644 drivers/target/sbp/sbp_fabric.c create mode 100644 drivers/target/sbp/sbp_fabric.h

drivers/target/sbp/sbp_fabric.c new file +0 -0
new file mode 100644
index 0000000..edc6fda

<snip>

Quoted 18 lines

drivers/target/sbp/sbp_fabric.c line 122

Quoted 13 lines
drivers/target/sbp/sbp_fabric.c new file +13 -0
+int sbp_new_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = transport_generic_allocate_tasks(se_cmd, req->cmd_buf);
+	if (ret)
+		return ret;
+
+	return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, NULL, 0);
+}
+

Because sbp_scsi_cmnd.c:sbp_handle_command() is using the new target_submit_cmd() logic, target-core will not be calling TFO->new_cmd_map() -> sbp_new_cmd() for setup here.

Quoted 4 lines
Quoted 1 line

Go ahead and drop this now.

So I can just set TFO->new_cmd_map to NULL?

Just drop the sbp_ops->new_cmd_map assignment, no need to explictly set it to NULL..

Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
drivers/target/sbp/sbp_fabric.c new file +6 -0
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
Quoted 8 lines

drivers/target/sbp/sbp_fabric.c line 225

Quoted 4 lines
drivers/target/sbp/sbp_fabric.c new file +4 -0
+	/* only used for printk and family? */
+	return (u32)req->orb_pointer;
+}
+

So an the ABORT_TASK TMR patches use TFO->get_task_tag() to locate a referenced tag to locate the se_cmd descriptor. Since we don't support TMRs yet in sbp, this value will only be used for

informational

Quoted 8 lines
Quoted 1 line

purposes.

I'd like to support ABORT_TASK and friends at some point. The problem I see here though is that tags in SBP-2/3 are 48-bits wide (the ORB address in the initiator node's memory), and the target framework seems to use a u32 for this. SAM-3 Section 4.11 seems to say "A task tag is a value that is composed of up to 64 bits" so it might be worth getting that changed in the target framework?

Bumping TFO->get_task_tag and se_tmr_req->ref_task_tag to u64 is easy enough, but it would make sense to figure out if this is really required in practice first..

--nab

author maintainer
#30 lore raw

PATCH 7/13 firewire-sbp-target: Add sbp_proto.{c,h}

In reply to Chris Boot

These are functions to generate TransportID identifiers as per SPC-4 revision 17.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_proto.c |  113 ++++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_proto.h |   12 ++++
 2 files changed, 125 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_proto.c
 create mode 100644 drivers/target/sbp/sbp_proto.h
drivers/target/sbp/sbp_proto.c new file +113 -0
new file mode 100644
index 0000000..5744b5c
@@ -0,0 +1,113 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <scsi/scsi.h>
+#include <target/target_core_base.h>
+
+#include "sbp_proto.h"
+
+/*
+ * Handlers for Serial Attached SCSI (SBP)
+ */
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+	/*
+	 * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+	 * This is defined in section 7.5.1 Table 362 in spc4r17
+	 */
+	return SCSI_PROTOCOL_SBP;
+}
+EXPORT_SYMBOL(sbp_get_fabric_proto_ident);
+
+u32 sbp_get_pr_transport_id(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code,
+	unsigned char *buf)
+{
+	int ret;
+
+	/*
+	 * Set PROTOCOL IDENTIFIER to 3h for SBP
+	 */
+	buf[0] = SCSI_PROTOCOL_SBP;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 */
+	ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+	if (ret < 0)
+		pr_debug("sbp transport_id: invalid hex string\n");
+
+	/*
+	 * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+EXPORT_SYMBOL(sbp_get_pr_transport_id);
+
+u32 sbp_get_pr_transport_id_len(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code)
+{
+	*format_code = 0;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 *
+	 * The SBP Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+EXPORT_SYMBOL(sbp_get_pr_transport_id_len);
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+char *sbp_parse_pr_out_transport_id(
+	struct se_portal_group *se_tpg,
+	const char *buf,
+	u32 *out_tid_len,
+	char **port_nexus_ptr)
+{
+	/*
+	 * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+	 * for initiator ports using SCSI over SBP Serial SCSI Protocol
+	 *
+	 * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+	 * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+	 * so we return the **port_nexus_ptr set to NULL.
+	 */
+	*port_nexus_ptr = NULL;
+	*out_tid_len = 24;
+
+	return (char *)&buf[8];
+}
+EXPORT_SYMBOL(sbp_parse_pr_out_transport_id);
+
drivers/target/sbp/sbp_proto.h new file +12 -0
new file mode 100644
index 0000000..1bd3e16
@@ -0,0 +1,12 @@
+
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg);
+u32 sbp_get_pr_transport_id(struct se_portal_group *se_tpg,
+		struct se_node_acl *se_nacl, struct t10_pr_registration *pr_reg,
+		int *format_code, unsigned char *buf);
+u32 sbp_get_pr_transport_id_len(
+		struct se_portal_group *se_tpg, struct se_node_acl *se_nacl,
+		struct t10_pr_registration *pr_reg, int *format_code);
+char *sbp_parse_pr_out_transport_id(
+		struct se_portal_group *se_tpg, const char *buf,
+		u32 *out_tid_len, char **port_nexus_ptr);
+
Signature · 1 line
1.7.9
author maintainer
#31 lore raw

PATCH 8/13 firewire-sbp-target: add sbp_management_agent.{c,h}

In reply to Chris Boot

This code implements the SBP-2 Management Agent. This is the first of two firewire address handlers that are used to communicate with the target. The Management Agent is used to handle login, reconnect and logout to a SCSI LUN as well as task management functions.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_management_agent.c |  266 +++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_management_agent.h |   23 +++
 2 files changed, 289 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_management_agent.c
 create mode 100644 drivers/target/sbp/sbp_management_agent.h
drivers/target/sbp/sbp_management_agent.c new file +266 -0
new file mode 100644
index 0000000..d9a2124
@@ -0,0 +1,266 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kref.h>
+
+#include <target/target_core_base.h>
+
+#include "../../firewire/core.h"
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_util.h"
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+	struct sbp_management_agent *agent =
+		container_of(work, struct sbp_management_agent, work);
+	struct sbp_management_request *req = agent->request;
+	int ret;
+	int status_data_len = 0;
+
+	/* fetch the ORB from the initiator */
+	ret = fw_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		agent->orb_offset, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb fetch failed: %x\n", ret);
+		goto out;
+	}
+
+	pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x "
+		"status_fifo:0x%llx\n",
+		sbp2_pointer_to_addr(&req->orb.ptr1),
+		sbp2_pointer_to_addr(&req->orb.ptr2),
+		be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+		sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+	/* sanity check basic fields */
+	if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+		ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+		pr_err("mgt_orb bad request\n");
+		goto out;
+	}
+
+	switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+	case MANAGEMENT_ORB_FUNCTION_LOGIN:
+		sbp_management_request_login(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+		sbp_management_request_query_logins(agent, req,
+				&status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+		sbp_management_request_reconnect(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+		pr_notice("SET PASSWORD not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+		sbp_management_request_logout(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+		pr_notice("ABORT TASK not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+		pr_notice("ABORT TASK SET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+		pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+		pr_notice("TARGET RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	default:
+		pr_notice("unknown management function 0x%x\n",
+			MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+	}
+
+	/* set up the status block we'll send to the initiator */
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+		STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+		STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+	/* write the status block back to the initiator */
+	ret = fw_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		sbp2_pointer_to_addr(&req->orb.status_fifo),
+		&req->status, 8 + status_data_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb status write failed: %x\n", ret);
+		goto out;
+	}
+
+out:
+	fw_card_put(req->card);
+	kfree(req);
+	atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_management_agent *agent = callback_data;
+	struct sbp2_pointer *ptr = data;
+
+	if (!agent->tport->enable) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if ((offset != agent->handler.offset) || (length != 8)) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		struct sbp_management_request *req;
+		int ret;
+
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					MANAGEMENT_AGENT_STATE_IDLE,
+					MANAGEMENT_AGENT_STATE_BUSY) !=
+				MANAGEMENT_AGENT_STATE_IDLE) {
+			pr_notice("ignoring management request while busy\n");
+
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}
+
+		req = kzalloc(sizeof(*req), GFP_ATOMIC);
+		if (!req) {
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}
+
+		req->card = fw_card_get(card);
+		req->generation = generation;
+		req->node_addr = source;
+		req->speed = fw_get_request_speed(request);
+
+		agent->orb_offset = sbp2_pointer_to_addr(ptr);
+		agent->request = req;
+
+		ret = queue_work(fw_workqueue, &agent->work);
+		if (!ret) {
+			/* pretend we're busy */
+			kfree(req);
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}
+
+		fw_send_response(card, request, RCODE_COMPLETE);
+	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+		addr_to_sbp2_pointer(agent->orb_offset, ptr);
+		fw_send_response(card, request, RCODE_COMPLETE);
+	} else {
+		fw_send_response(card, request, RCODE_TYPE_ERROR);
+	}
+}
+
+struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport)
+{
+	int ret;
+	struct sbp_management_agent *agent;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	agent->tport = tport;
+	agent->handler.length = 0x08;
+	agent->handler.address_callback = sbp_mgt_agent_rw;
+	agent->handler.callback_data = agent;
+	atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+	INIT_WORK(&agent->work, sbp_mgt_agent_process);
+	agent->orb_offset = 0;
+	agent->request = NULL;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+			&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+	if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}
+
drivers/target/sbp/sbp_management_agent.h new file +23 -0
new file mode 100644
index 0000000..615b90e
@@ -0,0 +1,23 @@
+
+struct sbp_management_agent {
+	struct sbp_tport *tport;
+	struct fw_address_handler handler;
+	atomic_t state;
+	struct work_struct work;
+	u64 orb_offset;
+	struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+	struct sbp_management_orb orb;
+	struct sbp_status_block status;
+	struct fw_card *card;
+	int generation;
+	int node_addr;
+	int speed;
+};
+
+struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport);
+void sbp_management_agent_unregister(struct sbp_management_agent *agent);
+
Signature · 1 line
1.7.9
author maintainer
#32 lore raw

PATCH 9/13 firewire-sbp-target: Add sbp_login.{c,h}

In reply to Chris Boot

This file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_login.c |  637 ++++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_login.h |   14 +
 2 files changed, 651 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_login.c
 create mode 100644 drivers/target/sbp/sbp_login.h
drivers/target/sbp/sbp_login.c new file +637 -0
new file mode 100644
index 0000000..fb1accf
@@ -0,0 +1,637 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "../../firewire/core.h"
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_util.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+	int ret;
+	__be32 high, low;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+			&high, sizeof(high));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+			&low, sizeof(low));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	*guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+	return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+	struct sbp_tpg *tpg, u64 guid)
+{
+	struct se_session *se_sess;
+
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		if (sess->guid == guid)
+			return sess;
+	}
+
+	return NULL;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+		struct sbp_session *session, struct se_lun *lun)
+{
+	struct sbp_login_descriptor *login;
+
+	list_for_each_entry(login, &session->login_list, link) {
+		if (login->lun == lun)
+			return login;
+	}
+
+	return NULL;
+}
+
+static int sbp_login_count_all_by_lun(
+		struct sbp_tpg *tpg,
+		struct se_lun *lun,
+		int exclusive)
+{
+	struct se_session *se_sess;
+	int count = 0;
+
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		struct sbp_login_descriptor *login;
+
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->lun != lun)
+				continue;
+
+			if (!exclusive) {
+				count++;
+				continue;
+			}
+
+			if (login->exclusive)
+				count++;
+		}
+	}
+
+	return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+	struct sbp_tpg *tpg, int login_id)
+{
+	struct se_session *se_sess;
+
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		struct sbp_login_descriptor *login;
+
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->login_id == login_id)
+				return login;
+		}
+	}
+
+	return NULL;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+	struct se_portal_group *se_tpg = &tpg->se_tpg;
+	struct se_lun *se_lun;
+
+	if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+		return ERR_PTR(-ENODEV);
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+	se_lun = &se_tpg->tpg_lun_list[lun];
+
+	if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+		se_lun = ERR_PTR(-EINVAL);
+
+	spin_unlock(&se_tpg->tpg_lun_lock);
+
+	return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+		struct sbp_tpg *tpg,
+		u64 guid)
+{
+	struct sbp_session *sess;
+	int ret;
+	char guid_str[17];
+	struct se_node_acl *se_nacl;
+
+	sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+	if (!sess) {
+		pr_err("failed to allocate session descriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sess->se_sess = transport_init_session();
+	if (IS_ERR(sess->se_sess)) {
+		pr_err("failed to init se_session\n");
+
+		ret = PTR_ERR(sess->se_sess);
+		kfree(sess);
+		return ERR_PTR(ret);
+	}
+
+	snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+	se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+	if (!se_nacl) {
+		pr_warn("Node ACL not found for %s\n", guid_str);
+
+		transport_free_session(sess->se_sess);
+		kfree(sess);
+
+		return ERR_PTR(-EPERM);
+	}
+
+	INIT_LIST_HEAD(&sess->login_list);
+	sess->se_sess->se_node_acl = se_nacl;
+	INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+	sess->guid = guid;
+
+	transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+	return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+	if (!list_empty(&sess->login_list))
+		return;
+
+	transport_deregister_session_configfs(sess->se_sess);
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->card)
+		fw_card_put(sess->card);
+
+	if (cancel_work)
+		cancel_delayed_work_sync(&sess->maint_work);
+
+	kfree(sess);
+}
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+	bool cancel_work)
+{
+	/* FIXME: abort/wait on tasks */
+
+	list_del(&login->link);
+	sbp_target_agent_unregister(login->tgt_agt);
+	sbp_session_release(login->sess, cancel_work);
+	kfree(login);
+}
+
+void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	struct se_lun *lun;
+	int ret;
+	u64 guid;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login;
+	struct sbp_login_response_block *response;
+	int login_response_len;
+
+	/* find the LUN we want to login to */
+	lun = sbp_get_lun_from_tpg(tpg,
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+	if (IS_ERR(lun)) {
+		pr_notice("login to unknown LUN: %d\n",
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+		return;
+	}
+
+	/* read the peer's GUID */
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+		lun->unpacked_lun, guid);
+
+	/* locate an existing session if there is one */
+	sess = sbp_session_find_by_guid(tpg, guid);
+
+	/*
+	 * check for any existing logins by comparing GUIDs
+	 * reject with access_denied if present
+	 */
+	if (sess) {
+		login = sbp_login_find_by_lun(sess, lun);
+		if (login) {
+			pr_notice("initiator already logged-in\n");
+
+			/*
+			 * SBP-2 R4 says we should return access denied, but
+			 * that can confuse initiators. Instead we need to
+			 * treat this like a reconnect, but send the login
+			 * response block like a fresh login.
+			 */
+
+			goto already_logged_in;
+		}
+	}
+
+	/*
+	 * check exclusive bit in login request
+	 * reject with access_denied if any logins present
+	 */
+	if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+		sbp_login_count_all_by_lun(tpg, lun, 0)) {
+		pr_warn("refusing exclusive login with other active logins\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check exclusive bit in any existing login descriptor
+	 * reject with access_denied if any exclusive logins present
+	 */
+	if (sbp_login_count_all_by_lun(tpg, lun, 1)) {
+		pr_warn("refusing login while another exclusive login present\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check we haven't exceeded the number of allowed logins
+	 * reject with resources_unavailable if we have
+	 */
+	if (sbp_login_count_all_by_lun(tpg, lun, 0) >=
+			tport->max_logins_per_lun) {
+		pr_warn("max number of logins reached\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	if (!sess) {
+		sess = sbp_session_create(tpg, guid);
+		if (IS_ERR(sess)) {
+			switch (PTR_ERR(sess)) {
+			case -EPERM:
+				ret = SBP_STATUS_ACCESS_DENIED;
+				break;
+			default:
+				ret = SBP_STATUS_RESOURCES_UNAVAIL;
+				break;
+			}
+
+			req->status.status = cpu_to_be32(
+				STATUS_BLOCK_RESP(
+					STATUS_RESP_REQUEST_COMPLETE) |
+				STATUS_BLOCK_SBP_STATUS(ret));
+			return;
+		}
+
+		sess->node_id = req->node_addr;
+		sess->card = fw_card_get(req->card);
+		sess->generation = req->generation;
+		sess->speed = req->speed;
+
+		queue_delayed_work(fw_workqueue, &sess->maint_work,
+			SESSION_MAINTENANCE_INTERVAL);
+	}
+
+	/* only take the latest reconnect_hold into account */
+	sess->reconnect_hold = min(
+		1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+		tport->max_reconnect_timeout) - 1;
+
+	/* create new login descriptor */
+	login = kmalloc(sizeof(*login), GFP_KERNEL);
+	if (!login) {
+		pr_err("failed to allocate login descriptor\n");
+
+		sbp_session_release(sess, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login->sess = sess;
+	login->lun = lun;
+	login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+	login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+	login->login_id = atomic_inc_return(&login_id);
+	atomic_set(&login->unsolicited_status_enable, 0);
+
+	/* set up address handler */
+	login->tgt_agt = sbp_target_agent_register(login);
+	if (IS_ERR(login->tgt_agt)) {
+		ret = PTR_ERR(login->tgt_agt);
+		pr_err("failed to map command block handler: %d\n", ret);
+
+		sbp_session_release(sess, true);
+		kfree(login);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	/* add to logins list */
+	list_add_tail(&login->link, &sess->login_list);
+
+already_logged_in:
+	/* send login response */
+	response = kzalloc(sizeof(*response), GFP_KERNEL);
+	if (!response) {
+		pr_err("failed to allocate login response block\n");
+
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login_response_len = max(12, min((int)sizeof(response),
+		(int)LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length))));
+	response->misc = cpu_to_be32(
+		((login_response_len & 0xffff) << 16) |
+		(login->login_id & 0xffff));
+	response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+	addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+		&response->command_block_agent);
+
+	ret = fw_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		sbp2_pointer_to_addr(&req->orb.ptr2), response,
+		login_response_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("failed to write login response block: %x\n", ret);
+
+		kfree(response);
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	kfree(response);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	pr_notice("QUERY LOGINS not implemented\n");
+	/* FIXME: implement */
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int ret;
+	u64 guid;
+	struct sbp_login_descriptor *login;
+
+	/* read the peer's GUID */
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+	/* find the login */
+	login = sbp_login_find_by_id(tpg,
+		RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+	if (!login) {
+		pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	if (login->sess->guid != guid) {
+		pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	if (login->sess->card)
+		fw_card_put(login->sess->card);
+
+	/* update the node details */
+	login->sess->generation = req->generation;
+	login->sess->node_id = req->node_addr;
+	login->sess->card = fw_card_get(req->card);
+	login->sess->speed = req->speed;
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int login_id;
+	struct sbp_login_descriptor *login;
+
+	login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+	/* Find login by ID */
+	login = sbp_login_find_by_id(tpg, login_id);
+	if (!login) {
+		pr_warn("cannot find login: %d\n", login_id);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+		return;
+	}
+
+	pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+		login->lun->unpacked_lun, login->login_id);
+
+	/* Check source against login's node_id */
+	if (req->node_addr != login->sess->node_id) {
+		pr_warn("logout from different node ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/* Perform logout */
+	sbp_login_release(login, true);
+
+	pr_info("logout successful!\n");
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {
+		pr_info("Waiting for reconnect from node: %016llx\n",
+			sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+	struct sbp_login_descriptor *login, *temp;
+
+	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+	list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+		sbp_login_release(login, false);
+	}
+
+	/* sbp_login_release() calls sbp_session_release() */
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+	struct sbp_session *sess = container_of(work, struct sbp_session,
+		maint_work.work);
+
+	/* could be called while tearing down the session */
+	if (list_empty(&sess->login_list))
+		return;
+
+	if (sess->node_id != -1) {
+		/* check for bus reset and make node_id invalid */
+		session_check_for_reset(sess);
+
+		queue_delayed_work(fw_workqueue, &sess->maint_work,
+			SESSION_MAINTENANCE_INTERVAL);
+	} else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+		/* still waiting for reconnect */
+		queue_delayed_work(fw_workqueue, &sess->maint_work,
+			SESSION_MAINTENANCE_INTERVAL);
+	} else {
+		/* reconnect timeout has expired */
+		session_reconnect_expired(sess);
+	}
+}
+
drivers/target/sbp/sbp_login.h new file +14 -0
new file mode 100644
index 0000000..4c86dcb
@@ -0,0 +1,14 @@
+
+extern void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+
Signature · 1 line
1.7.9
author maintainer
#33 lore raw

PATCH 10/13 firewire-sbp-target: Add sbp_target_agent.{c,h}

In reply to Chris Boot

This implements the SBP-2 Command Block Agent, or Target Agent. This is what receives SCSI commands and forwards them to the target framework.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_target_agent.c |  360 +++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_target_agent.h |   30 +++
 2 files changed, 390 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_target_agent.c
 create mode 100644 drivers/target/sbp/sbp_target_agent.h
drivers/target/sbp/sbp_target_agent.c new file +360 -0
new file mode 100644
index 0000000..4ab2000
@@ -0,0 +1,360 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+#include "sbp_util.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_READ_QUADLET_REQUEST) {
+		__be32 state;
+
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		state = cpu_to_be32(atomic_read(&agent->state));
+		memcpy(data, &state, sizeof(state));
+
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		/* ignored */
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		pr_debug("tgt_agent AGENT_RESET\n");
+		atomic_set(&agent->state, AGENT_STATE_RESET);
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		int ret;
+
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();
+
+		agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+
+		pr_debug("tgt_agent ORB_POINTER write: %llu\n",
+			agent->orb_pointer);
+
+		ret = queue_work(fw_workqueue, &agent->work);
+		if (!ret)
+			return RCODE_CONFLICT_ERROR;
+
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+		pr_debug("tgt_agent ORB_POINTER READ\n");
+		addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		int ret;
+
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();
+
+		pr_debug("tgt_agent DOORBELL\n");
+
+		ret = queue_work(fw_workqueue, &agent->work);
+		if (!ret)
+			return RCODE_CONFLICT_ERROR;
+
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
+		pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+		atomic_set(&agent->login->unsolicited_status_enable, 1);
+		return RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+		return RCODE_COMPLETE;
+	} else {
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static void tgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_target_agent *agent = callback_data;
+	int rcode = RCODE_ADDRESS_ERROR;
+
+	/* turn offset into the offset from the start of the block */
+	offset -= agent->handler.offset;
+
+	/* check the source matches the login */
+	if (source != agent->login->sess->node_id) {
+		pr_notice("ignoring request from foreign node (%x != %x)\n",
+			source, agent->login->sess->node_id);
+		fw_send_response(card, request, RCODE_TYPE_ERROR);
+		return;
+	}
+
+	if (offset == 0x00 && length == 4) {
+		/* AGENT_STATE */
+		rcode = tgt_agent_rw_agent_state(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x04 && length == 4) {
+		/* AGENT_RESET */
+		rcode = tgt_agent_rw_agent_reset(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x08 && length == 8) {
+		/* ORB_POINTER */
+		rcode = tgt_agent_rw_orb_pointer(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x10 && length == 4) {
+		/* DOORBELL */
+		rcode = tgt_agent_rw_doorbell(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x14 && length == 4) {
+		/* UNSOLICITED_STATUS_ENABLE */
+		rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+			generation, data, agent);
+	}
+
+	fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+	struct sbp_target_request *req =
+		container_of(work, struct sbp_target_request, work);
+
+	switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+	case 0:/* Format specified by this standard */
+		sbp_handle_command(req);
+		return;
+	case 1: /* Reserved for future standardization */
+	case 2: /* Vendor-dependent */
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	case 3: /* Dummy ORB */
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_DUMMY_ORB_COMPLETE));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	default:
+		BUG();
+	}
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+	struct sbp_target_agent *agent =
+		container_of(work, struct sbp_target_agent, work);
+	struct sbp_session *sess = agent->login->sess;
+	struct sbp_target_request *req;
+	int ret;
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+		return;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		goto out;
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE) {
+		sbp_free_request(req);
+		return;
+	}
+
+	/* read in the ORB */
+	ret = fw_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		agent->orb_pointer, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("tgt_orb fetch failed: %x\n", ret);
+		sbp_free_request(req);
+		goto out;
+	}
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE) {
+		sbp_free_request(req);
+		return;
+	}
+
+	req->agent = agent;
+	req->orb_pointer = agent->orb_pointer;
+
+	pr_debug("tgt_orb ptr:0x%llx next_orb:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+			req->orb_pointer,
+			sbp2_pointer_to_addr(&req->orb.next_orb),
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			be32_to_cpu(req->orb.misc));
+
+	if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+		req->status.status = cpu_to_be32(
+				STATUS_BLOCK_SRC(STATUS_SRC_ORB_FINISHED));
+	} else {
+		req->status.status = cpu_to_be32(
+				STATUS_BLOCK_SRC(STATUS_SRC_ORB_CONTINUING));
+	}
+
+	req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_pointer >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_pointer & 0xfffffffc);
+	INIT_WORK(&req->work, tgt_agent_process_work);
+
+	ret = queue_work(fw_workqueue, &req->work);
+	if (!ret) {
+		pr_err("tgt_orb queue_work failed\n");
+		sbp_free_request(req);
+		goto out;
+	}
+
+	/* check if we should carry on processing */
+	if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+		/* null next_orb */
+		goto out;
+	}
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+		return;
+
+	agent->orb_pointer = sbp2_pointer_to_addr(&req->orb.next_orb);
+
+	if (!queue_work(fw_workqueue, &agent->work)) {
+		pr_err("tgt_orb fetch queue_work failed\n");
+		goto out;
+	}
+
+	return;
+
+out:
+	/* finished */
+	atomic_cmpxchg(&agent->state,
+		AGENT_STATE_ACTIVE, AGENT_STATE_SUSPENDED);
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login)
+{
+	struct sbp_target_agent *agent;
+	int ret;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	agent->handler.length = 0x20;
+	agent->handler.address_callback = tgt_agent_rw;
+	agent->handler.callback_data = agent;
+
+	agent->login = login;
+	atomic_set(&agent->state, AGENT_STATE_RESET);
+	INIT_WORK(&agent->work, tgt_agent_fetch_work);
+	agent->orb_pointer = (u64)-1;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+		&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}
+
drivers/target/sbp/sbp_target_agent.h new file +30 -0
new file mode 100644
index 0000000..870b901
@@ -0,0 +1,30 @@
+
+struct sbp_target_agent {
+	struct fw_address_handler handler;
+	struct sbp_login_descriptor *login;
+	atomic_t state;
+	struct work_struct work;
+	u64 orb_pointer;
+};
+
+struct sbp_target_request {
+	struct sbp_target_agent *agent;
+	u64 orb_pointer;
+	struct sbp_command_block_orb orb;
+	struct sbp_status_block status;
+	struct work_struct work;
+
+	struct se_cmd se_cmd;
+	struct sbp_page_table_entry *pg_tbl;
+	void *cmd_buf;
+	int unpacked_lun;
+	u32 data_len;
+	enum dma_data_direction	data_dir;
+	void *data_buf;
+
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
Signature · 1 line
1.7.9
author maintainer
#34 lore raw

PATCH 11/13 firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}

In reply to Chris Boot

Miscellaneous functions for dealing with SCSI commands, status, sense data and data read/write. This is where the real grunt work of pushing data in and out of the FireWire bus happens.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_scsi_cmnd.c |  357 ++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_scsi_cmnd.h |    6 +
 2 files changed, 363 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
 create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
drivers/target/sbp/sbp_scsi_cmnd.c new file +357 -0
new file mode 100644
index 0000000..ca94a28
@@ -0,0 +1,357 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "sbp_base.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+#include "sbp_util.h"
+
+/*
+ * Wraps fw_run_transaction taking into account page size and max payload, and
+ * retries the transaction if it fails
+ */
+static int sbp_run_transaction(struct sbp_target_request *req, int tcode,
+	unsigned long long offset, void *payload, size_t length)
+{
+	struct sbp_login_descriptor *login = req->agent->login;
+	struct sbp_session *sess = login->sess;
+	int ret, speed, max_payload, pg_size, seg_off = 0, seg_len;
+
+	speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+	max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+	pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+
+	if (pg_size) {
+		pr_err("sbp_run_transaction: page size ignored\n");
+		pg_size = 0x100 << pg_size;
+	}
+
+	while (seg_off < length) {
+		seg_len = length - seg_off;
+		if (seg_len > max_payload)
+			seg_len = max_payload;
+
+		/* FIXME: take page_size into account */
+
+		/* FIXME: retry failed data transfers */
+		ret = fw_run_transaction(sess->card, tcode,
+				sess->node_id, sess->generation, speed,
+				offset + seg_off, payload + seg_off, seg_len);
+		if (ret != RCODE_COMPLETE) {
+			pr_debug("sbp_run_transaction: txn failed: %x\n", ret);
+			return -EIO;
+		}
+
+		seg_off += seg_len;
+	}
+
+	return 0;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+	int ret, cmd_len, copy_len;
+
+	cmd_len = scsi_command_size(req->orb.command_block);
+
+	req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+	if (!req->cmd_buf)
+		return -ENOMEM;
+
+	memcpy(req->cmd_buf, req->orb.command_block,
+		min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+	if (cmd_len > sizeof(req->orb.command_block)) {
+		pr_debug("sbp_fetch_command: filling in long command\n");
+		copy_len = cmd_len - sizeof(req->orb.command_block);
+
+		ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+			req->orb_pointer + sizeof(req->orb),
+			req->cmd_buf + sizeof(req->orb.command_block), cmd_len);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+	int pg_tbl_sz, ret;
+	struct sbp_page_table_entry *pg_tbl;
+
+	if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+		return 0;
+
+	pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+		sizeof(struct sbp_page_table_entry);
+
+	pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+	if (!pg_tbl)
+		return -ENOMEM;
+
+	ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+		sbp2_pointer_to_addr(&req->orb.data_descriptor),
+		pg_tbl, pg_tbl_sz);
+	if (ret) {
+		kfree(pg_tbl);
+		return ret;
+	}
+
+	req->pg_tbl = pg_tbl;
+	return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req)
+{
+	int data_size, direction, idx;
+
+	data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+	direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+	if (!data_size) {
+		req->data_len = 0;
+		req->data_dir = DMA_NONE;
+		return;
+	}
+
+	req->data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+	if (req->pg_tbl) {
+		req->data_len = 0;
+		for (idx = 0; idx < data_size; idx++) {
+			req->data_len += be16_to_cpu(
+					req->pg_tbl[idx].segment_length);
+		}
+	} else {
+		req->data_len = data_size;
+	}
+}
+
+void sbp_handle_command(struct sbp_target_request *req)
+{
+	struct sbp_login_descriptor *login = req->agent->login;
+	struct sbp_session *sess = login->sess;
+	int ret;
+
+	ret = sbp_fetch_command(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	ret = sbp_fetch_page_table(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+			ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	req->unpacked_lun = req->agent->login->lun->unpacked_lun;
+	sbp_calc_data_length_direction(req);
+
+	pr_debug("sbp_handle_command unpacked_lun:%d data_len:%d "
+		"data_dir:%d\n", req->unpacked_lun, req->data_len,
+		req->data_dir);
+
+	target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+			req->sense_buf, req->unpacked_lun, 0, MSG_SIMPLE_TAG,
+			req->data_dir, TARGET_SCF_UNKNOWN_SIZE);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+int sbp_rw_data(struct sbp_target_request *req)
+{
+	int ret;
+
+	WARN_ON(!req->data_len);
+
+	if (req->pg_tbl) {
+		int idx, offset = 0, data_size;
+
+		data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+		for (idx = 0; idx < data_size; idx++) {
+			int pte_len = be16_to_cpu(
+				req->pg_tbl[idx].segment_length);
+			u64 pte_offset = (u64)be16_to_cpu(
+				req->pg_tbl[idx].segment_base_hi) << 32 |
+				be32_to_cpu(req->pg_tbl[idx].segment_base_lo);
+
+			ret = sbp_run_transaction(req,
+				(req->data_dir == DMA_TO_DEVICE) ?
+				TCODE_READ_BLOCK_REQUEST :
+				TCODE_WRITE_BLOCK_REQUEST,
+				pte_offset, req->data_buf + offset, pte_len);
+			if (ret)
+				break;
+
+			offset += pte_len;
+		}
+	} else {
+		ret = sbp_run_transaction(req,
+			(req->data_dir == DMA_TO_DEVICE) ?
+			TCODE_READ_BLOCK_REQUEST : TCODE_WRITE_BLOCK_REQUEST,
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			req->data_buf, req->data_len);
+	}
+
+	return ret;
+}
+
+int sbp_send_status(struct sbp_target_request *req)
+{
+	int ret, length;
+	struct sbp_login_descriptor *login = req->agent->login;
+
+	/* calculate how much data to send */
+	length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+	ret = sbp_run_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+		login->status_fifo_addr, &req->status, length);
+	if (ret) {
+		pr_debug("sbp_send_status: write failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+	u8 *sense = req->sense_buf;
+	u8 *status = req->status.data;
+
+	WARN_ON(se_cmd->scsi_sense_length < 18);
+
+	switch (sense[0] & 0x7f) {
+	case 0x70:
+		status[0] = 0 << 6;		/* sfmt */
+		break;
+	case 0x71:
+		status[0] = 1 << 6;		/* sfmt */
+		break;
+	default:
+		/*
+		 * TODO: SBP-3 specifies what we should do with descriptor
+		 * format sense data
+		 */
+		pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+			sense[0]);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+		return;
+	}
+
+	status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+	status[1] =
+		(sense[0] & 0x80) |		/* valid */
+		((sense[2] & 0xe0) >> 1) |	/* mark, eom, ili */
+		(sense[2] & 0x0f);		/* sense_key */
+	status[2] = se_cmd->scsi_asc;		/* sense_code */
+	status[3] = se_cmd->scsi_ascq;		/* sense_qualifier */
+
+	/* information */
+	status[4] = sense[3];
+	status[5] = sense[4];
+	status[6] = sense[5];
+	status[7] = sense[6];
+
+	/* CDB-dependent */
+	status[8] = sense[8];
+	status[9] = sense[9];
+	status[10] = sense[10];
+	status[11] = sense[11];
+
+	/* fru */
+	status[12] = sense[14];
+
+	/* sense_key-dependent */
+	status[13] = sense[15];
+	status[14] = sense[16];
+	status[15] = sense[17];
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_DEAD(0) |
+		STATUS_BLOCK_LEN(5) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+int sbp_send_sense(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+
+	if (se_cmd->scsi_sense_length) {
+		sbp_sense_mangle(req);
+	} else {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+	}
+
+	return sbp_send_status(req);
+}
+
+void sbp_free_request(struct sbp_target_request *req)
+{
+	kfree(req->pg_tbl);
+	kfree(req->cmd_buf);
+	kfree(req->data_buf);
+	kfree(req);
+}
drivers/target/sbp/sbp_scsi_cmnd.h new file +6 -0
new file mode 100644
index 0000000..5e82b25
@@ -0,0 +1,6 @@
+
+void sbp_handle_command(struct sbp_target_request *req);
+int sbp_rw_data(struct sbp_target_request *req);
+int sbp_send_status(struct sbp_target_request *req);
+int sbp_send_sense(struct sbp_target_request *req);
+void sbp_free_request(struct sbp_target_request *req);
Signature · 1 line
1.7.9
author maintainer
#35 lore raw

PATCH 12/13 firewire-sbp-target: Add sbp_util.{c,h}

In reply to Chris Boot

Common helper functions and global declarations.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_util.c |   36 ++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_util.h |   15 +++++++++++++++
 2 files changed, 51 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_util.c
 create mode 100644 drivers/target/sbp/sbp_util.h
drivers/target/sbp/sbp_util.c new file +36 -0
new file mode 100644
index 0000000..33073b1
@@ -0,0 +1,36 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_util.h"
+
+const struct fw_address_region sbp_register_region = {
+	.start = CSR_REGISTER_BASE + 0x10000,
+	.end   = 0x1000000000000ULL,
+};
+
drivers/target/sbp/sbp_util.h new file +15 -0
new file mode 100644
index 0000000..cd3e00d
@@ -0,0 +1,15 @@
+
+extern const struct fw_address_region sbp_register_region;
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+	return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+		(be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+	ptr->high = cpu_to_be32(addr >> 32);
+	ptr->low = cpu_to_be32(addr);
+}
+
Signature · 1 line
1.7.9
author maintainer
#36 lore raw

PATCH 13/13 firewire-sbp-target: Add to target Kconfig and Makefile

In reply to Chris Boot

This commit also adds an entry to the MAINTAINERS file.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 MAINTAINERS             |    9 +++++++++
 drivers/target/Kconfig  |    1 +
 drivers/target/Makefile |    1 +
 3 files changed, 11 insertions(+), 0 deletions(-)
MAINTAINERS +9 -0
index c0f348d..252aa72 100644
@@ -2695,6 +2695,15 @@ T:	git git://git.alsa-project.org/alsa-kernel.git
 S:	Maintained
 F:	sound/firewire/
 
+FIREWIRE SBP-2 TARGET
+M:  Chris Boot <[email protected]>
+L:  [email protected]
+L:  [email protected]
+L:  [email protected]
+T:  git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S:  Maintained
+F:  drivers/target/sbp/
+
 FIREWIRE SUBSYSTEM
 M:	Stefan Richter <[email protected]>
 L:	[email protected]
drivers/target/Kconfig +1 -0
index fc5fa9f..2cfa467 100644
@@ -41,5 +41,6 @@ source "drivers/target/tcm_fc/Kconfig"
 source "drivers/target/iscsi/Kconfig"
 source "drivers/target/tcm_vhost/Kconfig"
 source "drivers/target/usb-gadget/Kconfig"
+source "drivers/target/sbp/Kconfig"
 
 endif
drivers/target/Makefile +1 -0
index 6b5f526..1ae8862 100644
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC)		+= tcm_fc/
 obj-$(CONFIG_ISCSI_TARGET)	+= iscsi/
 obj-$(CONFIG_TCM_VHOST)		+= tcm_vhost/
 obj-$(CONFIG_TARGET_USB_GADGET)	+= usb-gadget/
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += sbp/
Signature · 1 line
1.7.9
#37 lore raw

Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

In reply to Chris Boot
Quoted 4 lines · On Feb 11 Chris Boot wrote:

Well after lots of work I have a working and generally (at least I think) sensible starting point for the FireWire target. It appears to work fine in all the configurations I've tested it against, including Linux and Mac OS X initiators.

I only very quickly scrolled through your patches yet. Looks all very readable and well laid out. Some random thoughts:

Some of the smaller source files could certainly be merged with other ones without loss of readability.

Some comments about what the code is doing can be removed since the function names are very well readable on their own. Example:
+ /* read the peer's GUID */
+ ret = read_peer_guid(&guid, req);

The APIs which you include from "../../firewire/core.h" should eventually be moved to <linux/firewire.h>. I think it does not matter whether this is done before or after mainline merge. When we do so we should check whether the affected APIs can be improved for usage in drivers.

Many of the printks should surely be demoted to debug messages with runtime on-and-off switch.

There are list traversals and list manipulations that make an impression as if they wanted to be mutex- or lock-protected, but I haven't looked yet in which contexts these accesses happen.

sbp_proto.c:
+/*
+ * Handlers for Serial Attached SCSI (SBP)
+ */

                 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Serial Bus Protocol

Though this is also readily apparent from the top comment in the file.

The four EXPORT_SYMBOLs in sbp_proto.c can be removed, it seems.

sbp_login.c:
+ pr_notice("initiator already logged-in\n");
+
+ /*
+ * SBP-2 R4 says we should return access denied, but
+ * that can confuse initiators. Instead we need to
+ * treat this like a reconnect, but send the login
+ * response block like a fresh login.
+ */
Are there initiators which don't bother with reconnect but send relogin straight away?

Kconfig:
"default n" is redundant.
"*older* Apple computers": They are still manufactured with this feature.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -==--
http://arcgraph.de/sr/
author maintainer
#38 lore raw

Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

Quoted 8 lines · Stefan Richter
Quoted 4 lines · On Feb 11 Chris Boot wrote:

Well after lots of work I have a working and generally (at least I think) sensible starting point for the FireWire target. It appears to work fine in all the configurations I've tested it against, including Linux and Mac OS X initiators.

I only very quickly scrolled through your patches yet. Looks all very readable and well laid out. Some random thoughts:

Thanks for taking a look!

Quoted 2 lines

Some of the smaller source files could certainly be merged with other ones without loss of readability.

Yes, definitely.

Review

Quoted 4 lines

Some comments about what the code is doing can be removed since the function names are very well readable on their own. Example:

(quoted diff) +2 -0
+	/* read the peer's GUID */
+	ret = read_peer_guid(&guid, req);

Guilty as charged. I'll go through and clean this up.

Quoted 4 lines

The APIs which you include from "../../firewire/core.h" should eventually be moved to<linux/firewire.h>. I think it does not matter whether this is done before or after mainline merge. When we do so we should check whether the affected APIs can be improved for usage in drivers.

I'm not even sure I use that much from there at all. Possibly only fw_card_{get,put,release}(), so that could quite easily be moved into <linux/firewire.h>.

Quoted 2 lines

Many of the printks should surely be demoted to debug messages with runtime on-and-off switch.

I've moved a lot of them to pr_debug() which doesn't emit anything unless you ask it to. Are there others you think should be pr_debug() that aren't?

Quoted 3 lines

There are list traversals and list manipulations that make an impression as if they wanted to be mutex- or lock-protected, but I haven't looked yet in which contexts these accesses happen.

Yes this is what I'm most concerned about but it's an area I know very little about. Some hand-holding would be appreciated.

Quoted 6 lines

sbp_proto.c:
+/*
+ * Handlers for Serial Attached SCSI (SBP)
+ */

                  ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Serial Bus Protocol

Though this is also readily apparent from the top comment in the file.

Hah. Can you tell where I copied and pasted from? :-) Fixed.

Quoted 1 line

The four EXPORT_SYMBOLs in sbp_proto.c can be removed, it seems.

Another copy & paste leftover. Also fixed.

Quoted 11 lines

sbp_login.c:

(quoted diff) +8 -0
+			pr_notice("initiator already logged-in\n");
+
+			/*
+			 * SBP-2 R4 says we should return access denied, but
+			 * that can confuse initiators. Instead we need to
+			 * treat this like a reconnect, but send the login
+			 * response block like a fresh login.
+			 */

Are there initiators which don't bother with reconnect but send relogin straight away?

I found my PowerBook did. It looks like when OpenFirmware hands over to Darwin, the latter just does a login. I changed this before I had the code to expire sessions once the reconnect timeout expires though so it's probably unnecessary now but it would slow down the boot by several seconds.

Quoted 3 lines

Kconfig:
"default n" is redundant.
"*older* Apple computers": They are still manufactured with this feature.

I thought all the newer ones only did this over Thunderbolt and not FireWire? I'm more than happy to change this though.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
#39 lore raw

Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target

Quoted 9 lines · On Feb 12 Chris Boot wrote:
Quoted 4 lines · Stefan Richter

The APIs which you include from "../../firewire/core.h" should eventually be moved to<linux/firewire.h>. I think it does not matter whether this is done before or after mainline merge. When we do so we should check whether the affected APIs can be improved for usage in drivers.

I'm not even sure I use that much from there at all. Possibly only fw_card_{get,put,release}(), so that could quite easily be moved into <linux/firewire.h>.

Yes, I suppose this is just the card reference counting which we found is needed for some types of userspace drivers too. But for them it is wrapped up in the core-cdev.c glue, thus still core-internal presently.

Quoted 6 lines
Quoted 2 lines

Many of the printks should surely be demoted to debug messages with runtime on-and-off switch.

I've moved a lot of them to pr_debug() which doesn't emit anything unless you ask it to. Are there others you think should be pr_debug() that aren't?

OK, could have been a wrong impression after superficial reading.

[...]

Quoted 9 lines
Quoted 9 lines

sbp_login.c:

(quoted diff) +8 -0
+			pr_notice("initiator already logged-in\n");
+
+			/*
+			 * SBP-2 R4 says we should return access denied, but
+			 * that can confuse initiators. Instead we need to
+			 * treat this like a reconnect, but send the login
+			 * response block like a fresh login.
+			 */
Quoted 8 lines
Quoted 2 lines

Are there initiators which don't bother with reconnect but send relogin straight away?

I found my PowerBook did. It looks like when OpenFirmware hands over to Darwin, the latter just does a login. I changed this before I had the code to expire sessions once the reconnect timeout expires though so it's probably unnecessary now but it would slow down the boot by several seconds.

Right, this is a special case. I remember this as an issue with Linux on Apple PCs as well, together with a particular harddisk firmware with somewhat unusual timing characteristics, which rejects firewire-sbp2's login because it still considers the firmware's login valid. The former ieee1394 + sbp2 stack had more luck, but probably just because of slightly different timing on their part.

You could make this a special case for initiators with Apple's OUI in the EUI-64. But I suppose there is no real downside to do this unconditionally.

Anyway, the case of Apple firmware login -> OS login handover could certainly be mentioned in the quoted comment, as the one quite common case where this deviation from the spec is required or at least beneficial.

Quoted 6 lines
Quoted 3 lines

Kconfig:
"default n" is redundant.
"*older* Apple computers": They are still manufactured with this feature.

I thought all the newer ones only did this over Thunderbolt and not FireWire? I'm more than happy to change this though.

Oh, I don't actually know about these ones.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -==--
http://arcgraph.de/sr/
author maintainer
#40 lore raw

v2 cover firewire-sbp-target: FireWire SBP-2 SCSI target

In reply to Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Changes since v1:
* Fixed some copy & paste issues
* Updated Kconfig (wording, depends, 'default n')
* Removed some unnecessary EXPORT_SYMBOL()s
* Merged sbp_util.{c,h} into sbp_configfs.c and sbp_base.h
* Merged sbp_proto.{c,h} into sbp_fabric.{c,h}
* Cleaned up comments and several printks
* Fixed a few minor bugs
* Create & use our own workqueue instead of using fw_workqueue
* Dropped the unused TFO->new_cmd_map and sbp_new_cmd()
* Overhauled and simplified tgt_agent_fetch_work()
* Removed some redundant members of struct sbp_target_request
* Removed struct sbp_lun and code to maintain redundant LUN list
* Added spinlock to struct sbp_session and use locking throughout
* Moved fw_card_{get,put,release}() into linux/firewire.h

It didn't feel like a lot of changes but actually that's quite a lot of work, looking back at it...

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected].
author maintainer
#41 lore raw

PATCH v2 1/11 firewire: Add function to get speed from opaque struct fw_request

Sometimes it's useful to know the FireWire speed of the request that has just come in to a fw_address_handler callback. As struct fw_request is opaque we can't peek inside to get the speed out of the struct fw_packet that's just inside. For example, the SBP-2 spec says:

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

[ ANSI T10/1155D Revision 4 page 53/54 ]

---
 drivers/firewire/core-transaction.c |   16 ++++++++++++++++
 include/linux/firewire.h            |    1 +
 2 files changed, 17 insertions(+), 0 deletions(-)
drivers/firewire/core-transaction.c +16 -0
index 855ab3f..614f592 100644
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
 }
 EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request:	The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
 static void handle_exclusive_region_request(struct fw_card *card,
 					    struct fw_packet *p,
 					    struct fw_request *request,
include/linux/firewire.h +1 -0
index 84ccf8e..f010307 100644
@@ -340,6 +340,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
 void fw_core_remove_address_handler(struct fw_address_handler *handler);
 void fw_send_response(struct fw_card *card,
 		      struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
 void fw_send_request(struct fw_card *card, struct fw_transaction *t,
 		     int tcode, int destination_id, int generation, int speed,
 		     unsigned long long offset, void *payload, size_t length,
Signature · 1 line
1.7.9
#42 lore raw

Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

Quoted 11 lines · On Feb 15 Chris Boot wrote:
drivers/firewire/core-transaction.c +5 -0
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
 }
 EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request:	The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at

drivers/firewire/core-transaction.c void fw_send_response(struct fw_card *card, · line 837

Quoted 10 lines
drivers/firewire/core-transaction.c +10 -0
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);

Uh oh, what have I done by asking for a comment? :-)

Can you tell what's wrong with this API documentation?

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#43 lore raw

Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

Quoted 25 lines · Stefan Richter
Quoted 11 lines · On Feb 15 Chris Boot wrote:
drivers/firewire/core-transaction.c +5 -0
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
  }
  EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request:	The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at

drivers/firewire/core-transaction.c void fw_send_response(struct fw_card *card, · line 837

Quoted 10 lines
drivers/firewire/core-transaction.c +10 -0
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);

Uh oh, what have I done by asking for a comment? :-)

Quoted 1 line

Can you tell what's wrong with this API documentation?

Better to have too much than too little? :-)

Linux 3.4, now with added Enterprise.

Shall I cut it down a bit?

Chris

Signature · 2 lines
Chris Boot
[email protected]
#44 lore raw

Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

Quoted 26 lines · On Feb 15 Chris Boot wrote:
Quoted 25 lines · Stefan Richter
Quoted 11 lines · On Feb 15 Chris Boot wrote:
drivers/firewire/core-transaction.c +5 -0
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
  }
  EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request:	The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at

drivers/firewire/core-transaction.c void fw_send_response(struct fw_card *card, · line 837

Quoted 10 lines
drivers/firewire/core-transaction.c +10 -0
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);

Uh oh, what have I done by asking for a comment? :-)

Quoted 7 lines
Quoted 1 line

Can you tell what's wrong with this API documentation?

Better to have too much than too little? :-)

Linux 3.4, now with added Enterprise.

Shall I cut it down a bit?

a) The implementation of the function should not be explained here; after all it is meant to be opaque to API users. Besides, if somebody changes the implementation he will for sure forget to change the comment.

b) It is fairly self-evident at which occasions an API user would want to use this function. (Everytime when he needs to know that speed.)

c) The function call argument does not really need to be explained either as soon as the purpose of the function has been made known.

So in my first response where I already acked your patch I should have simply asked for

/**
* fw_get_request_speed() - returns speed at which the @request was received */

to be added to your patch. :-)

Patch review could be so easy for everyone involved if the reviewer knew how to express himself...

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#45 lore raw

Re: [PATCH v2 01/11] firewire: Add function to get speed from opaque struct fw_request

Quoted 48 lines · Stefan Richter
Quoted 26 lines · On Feb 15 Chris Boot wrote:
Quoted 25 lines · Stefan Richter
Quoted 11 lines · On Feb 15 Chris Boot wrote:
drivers/firewire/core-transaction.c +5 -0
@@ -820,6 +820,22 @@ void fw_send_response(struct fw_card *card,
   }
   EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - Discover bus speed used for this request
+ * @request:	The struct fw_request from which to obtain the speed.
+ *
+ * In certain circumstances it's important to be able to obtain the speed at

drivers/firewire/core-transaction.c void fw_send_response(struct fw_card *card, · line 837

Quoted 10 lines
drivers/firewire/core-transaction.c +10 -0
+ * which a request was made to an address handler, for example when
+ * implementing an SBP-2 or SBP-3 target. This function inspects the response
+ * object to obtain the speed, which is copied from the request packet in
+ * allocate_request().
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);

Uh oh, what have I done by asking for a comment? :-)

Quoted 7 lines
Quoted 1 line

Can you tell what's wrong with this API documentation?

Better to have too much than too little? :-)

Linux 3.4, now with added Enterprise.

Shall I cut it down a bit?

a) The implementation of the function should not be explained here; after all it is meant to be opaque to API users. Besides, if somebody changes the implementation he will for sure forget to change the comment.

b) It is fairly self-evident at which occasions an API user would want to use this function. (Everytime when he needs to know that speed.)

c) The function call argument does not really need to be explained either as soon as the purpose of the function has been made known.

So in my first response where I already acked your patch I should have simply asked for

Quoted 8 lines

/**
* fw_get_request_speed() - returns speed at which the @request was received */

to be added to your patch. :-)

Patch review could be so easy for everyone involved if the reviewer knew how to express himself...

I guess it comes from me just trying too hard... Will fix for v3.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#46 lore raw

PATCH v2 2/11 firewire: Move fw_card kref functions into linux/firewire.h

In reply to Chris Boot

When writing a firewire driver that doesn't deal with struct fw_device objects (e.g. it only publishes FireWire units and doesn't subscribe to them), you likely need to keep referenced to struct fw_card objects so that you can send messages to other nodes. This patch moves fw_card_put(), fw_card_get() and fw_card_release() into the public include/linux/firewire.h header instead of drivers/firewire/core.h, and adds EXPORT_SYMBOL_GPL(fw_card_release).

The firewire-sbp-target module requires these so it can keep a reference to the fw_card object in order that it can fetch ORBs to execute and read/write related data and status information.

Cc Clemens Ladisch <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/firewire/core-card.c |    1 +
 drivers/firewire/core.h      |   15 ---------------
 include/linux/firewire.h     |   14 ++++++++++++++
 3 files changed, 15 insertions(+), 15 deletions(-)
drivers/firewire/core-card.c +1 -0
index 85661b0..42b180b 100644
@@ -654,6 +654,7 @@ void fw_card_release(struct kref *kref)
 
 	complete(&card->done);
 }
+EXPORT_SYMBOL_GPL(fw_card_release);
 
 void fw_core_remove_card(struct fw_card *card)
 {
drivers/firewire/core.h +0 -15
index b45be57..b44657b 100644
@@ -111,21 +111,6 @@ int fw_compute_block_crc(__be32 *block);
 void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
 void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
 
-static inline struct fw_card *fw_card_get(struct fw_card *card)
-{
-	kref_get(&card->kref);
-
-	return card;
-}
-
-void fw_card_release(struct kref *kref);
-
-static inline void fw_card_put(struct fw_card *card)
-{
-	kref_put(&card->kref, fw_card_release);
-}
-
-
 /* -cdev */
 
 extern const struct file_operations fw_device_ops;
include/linux/firewire.h +14 -0
index f010307..341e51c 100644
@@ -138,6 +138,20 @@ struct fw_card {
 	__be32 maint_utility_register;
 };
 
+static inline struct fw_card *fw_card_get(struct fw_card *card)
+{
+	kref_get(&card->kref);
+
+	return card;
+}
+
+void fw_card_release(struct kref *kref);
+
+static inline void fw_card_put(struct fw_card *card)
+{
+	kref_put(&card->kref, fw_card_release);
+}
+
 struct fw_attribute_group {
 	struct attribute_group *groups[2];
 	struct attribute_group group;
Signature · 1 line
1.7.9
Acked-by on v2 2/11
#47 lore raw

Re: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h

Quoted 8 lines · On Feb 15 Chris Boot wrote:

When writing a firewire driver that doesn't deal with struct fw_device objects (e.g. it only publishes FireWire units and doesn't subscribe to them), you likely need to keep referenced to struct fw_card objects so that you can send messages to other nodes. This patch moves fw_card_put(), fw_card_get() and fw_card_release() into the public include/linux/firewire.h header instead of drivers/firewire/core.h, and adds EXPORT_SYMBOL_GPL(fw_card_release).

Quoted 6 lines

The firewire-sbp-target module requires these so it can keep a reference to the fw_card object in order that it can fetch ORBs to execute and read/write related data and status information.

Cc Clemens Ladisch <[email protected]>
Quoted 69 lines
---
 drivers/firewire/core-card.c |    1 +
 drivers/firewire/core.h      |   15 ---------------
 include/linux/firewire.h     |   14 ++++++++++++++
 3 files changed, 15 insertions(+), 15 deletions(-)
drivers/firewire/core-card.c +1 -0
index 85661b0..42b180b 100644
@@ -654,6 +654,7 @@ void fw_card_release(struct kref *kref)
 
 	complete(&card->done);
 }
+EXPORT_SYMBOL_GPL(fw_card_release);
 
 void fw_core_remove_card(struct fw_card *card)
 {
drivers/firewire/core.h +0 -15
index b45be57..b44657b 100644
@@ -111,21 +111,6 @@ int fw_compute_block_crc(__be32 *block);
 void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
 void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
 
-static inline struct fw_card *fw_card_get(struct fw_card *card)
-{
-	kref_get(&card->kref);
-
-	return card;
-}
-
-void fw_card_release(struct kref *kref);
-
-static inline void fw_card_put(struct fw_card *card)
-{
-	kref_put(&card->kref, fw_card_release);
-}
-
-
 /* -cdev */
 
 extern const struct file_operations fw_device_ops;
include/linux/firewire.h +14 -0
index f010307..341e51c 100644
@@ -138,6 +138,20 @@ struct fw_card {
 	__be32 maint_utility_register;
 };
 
+static inline struct fw_card *fw_card_get(struct fw_card *card)
+{
+	kref_get(&card->kref);
+
+	return card;
+}
+
+void fw_card_release(struct kref *kref);
+
+static inline void fw_card_put(struct fw_card *card)
+{
+	kref_put(&card->kref, fw_card_release);
+}
+
 struct fw_attribute_group {
 	struct attribute_group *groups[2];
 	struct attribute_group group;
Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#48 lore raw

Re: [PATCH v2 02/11] firewire: Move fw_card kref functions into linux/firewire.h

Quoted 9 lines · Stefan Richter
Quoted 8 lines · On Feb 15 Chris Boot wrote:

When writing a firewire driver that doesn't deal with struct fw_device objects (e.g. it only publishes FireWire units and doesn't subscribe to them), you likely need to keep referenced to struct fw_card objects so that you can send messages to other nodes. This patch moves fw_card_put(), fw_card_get() and fw_card_release() into the public include/linux/firewire.h header instead of drivers/firewire/core.h, and adds EXPORT_SYMBOL_GPL(fw_card_release).

Quoted 8 lines
Quoted 6 lines

The firewire-sbp-target module requires these so it can keep a reference to the fw_card object in order that it can fetch ORBs to execute and read/write related data and status information.

Cc Clemens Ladisch<[email protected]>

Thanks!

[snip]

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#49 lore raw

PATCH v2 3/11 firewire-sbp-target: Add Kconfig, Makefile and TODO

In reply to Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/Kconfig  |   13 +++++++++++++
 drivers/target/sbp/Makefile |   11 +++++++++++
 drivers/target/sbp/TODO     |    7 +++++++
 3 files changed, 31 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/Kconfig
 create mode 100644 drivers/target/sbp/Makefile
 create mode 100644 drivers/target/sbp/TODO
drivers/target/sbp/Kconfig new file +13 -0
new file mode 100644
index 0000000..c9f85e9
@@ -0,0 +1,13 @@
+
+config FIREWIRE_SBP_TARGET
+	tristate "FireWire SBP-2 fabric module"
+	depends on FIREWIRE && EXPERIMENTAL
+	help
+	  Say Y or M here to enable SCSI target functionality over FireWire.
+	  This enables you to expose SCSI devices to other nodes on the FireWire
+	  bus, for example hard disks. Similar to FireWire Target Disk mode on
+	  many Apple computers.
+
+	  To compile this driver as a module, say M here: The module will be
+	  called firewire-sbp-target.
+
drivers/target/sbp/Makefile new file +11 -0
new file mode 100644
index 0000000..c8f73f4
@@ -0,0 +1,11 @@
+
+firewire-sbp-target-y += \
+	sbp_configfs.o \
+	sbp_fabric.o \
+	sbp_login.o \
+	sbp_management_agent.o \
+	sbp_scsi_cmnd.o \
+	sbp_target_agent.o
+
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += firewire-sbp-target.o
+
drivers/target/sbp/TODO new file +7 -0
new file mode 100644
index 0000000..eaec1c9
@@ -0,0 +1,7 @@
+* Force-terminate sessions when disabling targets
+* Ability to have several SCSI commands in-flight (TCQ?)
+* Retry failed FireWire transactions a few times with exponential backoff
+* Take into account the page_size field for transfers and/or page tables
+* Handle descriptor format sense data
+* Implement QUERY LOGINS management ORB
+* Implement TASK MANAGEMENT functionality
Signature · 1 line
1.7.9
author maintainer
#50 lore raw

PATCH v2 4/11 firewire-sbp-target: Add sbp_base.h header

In reply to Chris Boot

This header contains defines and structures that are common to many of the modules of the target code. This includes SBP-2 protocol structures and constants as well as a few structs for setting up the target, LUN login information and session setup.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_base.h |  204 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 204 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_base.h
drivers/target/sbp/sbp_base.h new file +204 -0
new file mode 100644
index 0000000..d39b743
@@ -0,0 +1,204 @@
+
+#define SBP_VERSION  "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE	8
+
+#define MANAGEMENT_AGENT_STATE_IDLE	0
+#define MANAGEMENT_AGENT_STATE_BUSY	1
+
+#define ORB_NOTIFY(v)			(((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v)		(((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v)	(((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN			0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS		0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT		0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD		0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT			0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK		0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET		0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET	0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET		0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v)		(((v) >> 28) &   0x01)
+#define LOGIN_ORB_RESERVED(v)		(((v) >> 24) &   0x0f)
+#define LOGIN_ORB_RECONNECT(v)		(((v) >> 20) &   0x0f)
+#define LOGIN_ORB_LUN(v)		(((v) >>  0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v)	(((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v)	(((v) >>  0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v)	(((v) >>  0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v)		(((v) >>  0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v)		(((v) >> 27) &   0x01)
+#define CMDBLK_ORB_SPEED(v)		(((v) >> 24) &   0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v)	(((v) >> 20) &   0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v)	(((v) >> 19) &   0x01)
+#define CMDBLK_ORB_PG_SIZE(v)		(((v) >> 16) &   0x07)
+#define CMDBLK_ORB_DATA_SIZE(v)		(((v) >>  0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v)		(((v) &   0x03) << 30)
+#define STATUS_BLOCK_RESP(v)		(((v) &   0x03) << 28)
+#define STATUS_BLOCK_DEAD(v)		(((v) ? 1 : 0)  << 27)
+#define STATUS_BLOCK_LEN(v)		(((v) &   0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v)	(((v) &   0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v)	(((v) & 0xffff) <<  0)
+
+#define STATUS_SRC_ORB_CONTINUING	0
+#define STATUS_SRC_ORB_FINISHED		1
+#define STATUS_SRC_UNSOLICITED		2
+
+#define STATUS_RESP_REQUEST_COMPLETE	0
+#define STATUS_RESP_TRANSPORT_FAILURE	1
+#define STATUS_RESP_ILLEGAL_REQUEST	2
+#define STATUS_RESP_VENDOR_DEPENDENT	3
+
+#define SBP_STATUS_OK			0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP	1
+#define SBP_STATUS_SPEED_NOTSUPP	2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP	3
+#define SBP_STATUS_ACCESS_DENIED	4
+#define SBP_STATUS_LUN_NOTSUPP		5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL	6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL	8
+#define SBP_STATUS_FUNCTION_REJECTED	9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN	10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE	11
+#define SBP_STATUS_REQUEST_ABORTED	12
+#define SBP_STATUS_UNSPECIFIED_ERROR	0xff
+
+#define AGENT_STATE_RESET	0
+#define AGENT_STATE_ACTIVE	1
+#define AGENT_STATE_SUSPENDED	2
+#define AGENT_STATE_DEAD	3
+
+struct sbp2_pointer {
+	__be32 high;
+	__be32 low;
+};
+
+struct sbp_command_block_orb {
+	struct sbp2_pointer next_orb;
+	struct sbp2_pointer data_descriptor;
+	__be32 misc;
+	u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+	__be16 segment_length;
+	__be16 segment_base_hi;
+	__be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+	struct sbp2_pointer ptr1;
+	struct sbp2_pointer ptr2;
+	__be32 misc;
+	__be32 length;
+	struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+	__be32 status;
+	__be32 orb_low;
+	u8 data[24];
+};
+
+struct sbp_login_response_block {
+	__be32 misc;
+	struct sbp2_pointer command_block_agent;
+	__be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+	struct sbp_session *sess;
+	struct list_head link;
+
+	struct se_lun *lun;
+
+	u64 status_fifo_addr;
+	int exclusive;
+	u16 login_id;
+	atomic_t unsolicited_status_enable;
+
+	struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+	struct se_session *se_sess;
+	struct list_head login_list;
+	spinlock_t login_list_lock;
+	struct delayed_work maint_work;
+
+	u64 guid; /* login_owner_EUI_64 */
+	int node_id; /* login_owner_ID */
+
+	struct fw_card *card;
+	int generation;
+	int speed;
+
+	int reconnect_hold;
+	u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+	/* Initiator EUI-64 */
+	u64 guid;
+	/* ASCII formatted GUID for SBP Initiator port */
+	char iport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_nodeacl() */
+	struct se_node_acl se_node_acl;
+};
+
+struct sbp_tpg {
+	/* Target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer back to sbp_tport */
+	struct sbp_tport *tport;
+	/* Returned by sbp_make_tpg() */
+	struct se_portal_group se_tpg;
+};
+
+struct sbp_tport {
+	/* Target Unit Identifier (EUI-64) */
+	u64 guid;
+	/* Target port name */
+	char tport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_tport() */
+	struct se_wwn tport_wwn;
+
+	struct sbp_tpg *tpg;
+
+	/* FireWire unit directory */
+	struct fw_descriptor unit_directory;
+
+	/* SBP Management Agent */
+	struct sbp_management_agent *mgt_agt;
+
+	/* Parameters */
+	int enable;
+	s32 directory_id;
+	int mgt_orb_timeout;
+	int max_reconnect_timeout;
+	int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+extern const struct fw_address_region sbp_register_region;
+extern struct workqueue_struct *sbp_workqueue;
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+	return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+		(be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+	ptr->high = cpu_to_be32(addr >> 32);
+	ptr->low = cpu_to_be32(addr);
+}
+
Signature · 1 line
1.7.9
#51 lore raw

Re: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header

Quoted 3 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_base.h new file +0 -0
@@ -0,0 +1,204 @@

[...]

Quoted 4 lines
drivers/target/sbp/sbp_base.h new file +4 -0
+struct sbp2_pointer {
+	__be32 high;
+	__be32 low;
+};

[...]

Quoted 17 lines
drivers/target/sbp/sbp_base.h new file +17 -0
+struct sbp_tport {
+	/* Target Unit Identifier (EUI-64) */
+	u64 guid;
+	/* Target port name */
+	char tport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_tport() */
+	struct se_wwn tport_wwn;
+
+	struct sbp_tpg *tpg;
+
+	/* FireWire unit directory */
+	struct fw_descriptor unit_directory;
+
+	/* SBP Management Agent */
+	struct sbp_management_agent *mgt_agt;
+
+	/* Parameters */

drivers/target/sbp/sbp_base.h line 191

Quoted 10 lines
drivers/target/sbp/sbp_base.h new file +10 -0
+	int enable;
+	s32 directory_id;
+	int mgt_orb_timeout;
+	int max_reconnect_timeout;
+	int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+extern const struct fw_address_region sbp_register_region;
+extern struct workqueue_struct *sbp_workqueue;

[...]

Generally, any source file (.c or .h) is meant to be self-contained WRT type definitions etc., meaning that they should have all required #include's in themselves rather than rely on indirect includes.

So here you should include the headers which define __be32, u64, struct se_wwn, struct workqueue and so on.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#52 lore raw

Re: [PATCH v2 04/11] firewire-sbp-target: Add sbp_base.h header

Quoted 39 lines · Stefan Richter
Quoted 3 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_base.h new file +0 -0
@@ -0,0 +1,204 @@

[...]

Quoted 4 lines
drivers/target/sbp/sbp_base.h new file +4 -0
+struct sbp2_pointer {
+	__be32 high;
+	__be32 low;
+};

[...]

Quoted 27 lines
drivers/target/sbp/sbp_base.h new file +27 -0
+struct sbp_tport {
+	/* Target Unit Identifier (EUI-64) */
+	u64 guid;
+	/* Target port name */
+	char tport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_tport() */
+	struct se_wwn tport_wwn;
+
+	struct sbp_tpg *tpg;
+
+	/* FireWire unit directory */
+	struct fw_descriptor unit_directory;
+
+	/* SBP Management Agent */
+	struct sbp_management_agent *mgt_agt;
+
+	/* Parameters */
+	int enable;
+	s32 directory_id;
+	int mgt_orb_timeout;
+	int max_reconnect_timeout;
+	int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+extern const struct fw_address_region sbp_register_region;
+extern struct workqueue_struct *sbp_workqueue;

[...]

Quoted 6 lines

Generally, any source file (.c or .h) is meant to be self-contained WRT type definitions etc., meaning that they should have all required #include's in themselves rather than rely on indirect includes.

So here you should include the headers which define __be32, u64, struct se_wwn, struct workqueue and so on.

Thanks. I'll work on fixing this.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#53 lore raw

PATCH v2 5/11 firewire-sbp-target: Add sbp_configfs.c

In reply to Chris Boot

This is used to glue the target framework's configfs code to the target code, and what is used to create targets and link them to LUNs to export. The code to create the FireWire unit directory to advertise targets on the FireWire bus is also in here.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_configfs.c |  751 +++++++++++++++++++++++++++++++++++++
 1 files changed, 751 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_configfs.c
drivers/target/sbp/sbp_configfs.c new file +751 -0
new file mode 100644
index 0000000..f4a6b36
@@ -0,0 +1,751 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+struct workqueue_struct *sbp_workqueue;
+
+/* FireWire address region for management and command block address handlers */
+const struct fw_address_region sbp_register_region = {
+	.start = CSR_REGISTER_BASE + 0x10000,
+	.end   = 0x1000000000000ULL,
+};
+
+static const u32 sbp_unit_directory_template[] = {
+	0x1200609e, /* unit_specifier_id: NCITS/T10 */
+	0x13010483, /* unit_sw_version: 1155D Rev 4 */
+	0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+	0x390104d8, /* command_set: SPC-2 */
+	0x3b000000, /* command_set_revision: 0 */
+	0x3c000001, /* firmware_revision: 1 */
+};
+
+static int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
+{
+	int i, count = 0;
+
+	spin_lock(&tpg->tpg_lun_lock);
+	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+		struct se_lun *se_lun = &tpg->tpg_lun_list[i];
+
+		if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+			continue;
+
+		count++;
+	}
+	spin_unlock(&tpg->tpg_lun_lock);
+
+	return count;
+}
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+	int num_luns, num_entries, idx = 0, mgt_agt_addr, ret, i;
+	u32 *data;
+
+	if (tport->unit_directory.data) {
+		fw_core_remove_descriptor(&tport->unit_directory);
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	if (!tport->enable || !tport->tpg)
+		return 0;
+
+	num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
+
+	/*
+	 * Number of entries in the final unit directory:
+	 *  - all of those in the template
+	 *  - management_agent
+	 *  - unit_characteristics
+	 *  - reconnect_timeout
+	 *  - unit unique ID
+	 *  - one for each LUN
+	 *
+	 *  MUST NOT include leaf or sub-directory entries
+	 */
+	num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+	if (tport->directory_id != -1)
+		num_entries++;
+
+	/* allocate num_entries + 4 for the header and unique ID leaf */
+	data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* directory_length */
+	data[idx++] = num_entries << 16;
+
+	/* directory_id */
+	if (tport->directory_id != -1)
+		data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+	/* unit directory template */
+	memcpy(&data[idx], sbp_unit_directory_template,
+			sizeof(sbp_unit_directory_template));
+	idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+	/* management_agent */
+	mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+	data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+	/* unit_characteristics */
+	data[idx++] = 0x3a000000 |
+		(((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+		SBP_ORB_FETCH_SIZE;
+
+	/* reconnect_timeout */
+	data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+	/* unit unique ID (leaf is just after LUNs) */
+	data[idx++] = 0x8d000000 | (num_luns + 1);
+
+	spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+		struct se_lun *se_lun = &tport->tpg->se_tpg.tpg_lun_list[i];
+		struct se_device *dev;
+		int type;
+
+		if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+			continue;
+
+		spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+		dev = se_lun->lun_se_dev;
+		type = dev->transport->get_device_type(dev);
+
+		/* logical_unit_number */
+		data[idx++] = 0x14000000 |
+			((type << 16) & 0x1f0000) |
+			(se_lun->unpacked_lun & 0xffff);
+
+		spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+	}
+	spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+	/* unit unique ID leaf */
+	data[idx++] = 2 << 16;
+	data[idx++] = tport->guid >> 32;
+	data[idx++] = tport->guid;
+
+	tport->unit_directory.length = idx;
+	tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+	tport->unit_directory.data = data;
+
+	ret = fw_core_add_descriptor(&tport->unit_directory);
+	if (ret < 0) {
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+	const char *cp;
+	char c, nibble;
+	int pos = 0, err;
+
+	*wwn = 0;
+	for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+		c = *cp;
+		if (c == '\n' && cp[1] == '\0')
+			continue;
+		if (c == '\0') {
+			err = 2;
+			if (pos != 16)
+				goto fail;
+			return cp - name;
+		}
+		err = 3;
+		if (isdigit(c))
+			nibble = c - '0';
+		else if (isxdigit(c) && (islower(c) || !strict))
+			nibble = tolower(c) - 'a' + 10;
+		else
+			goto fail;
+		*wwn = (*wwn << 4) | nibble;
+		pos++;
+	}
+	err = 4;
+fail:
+	printk(KERN_INFO "err %u len %zu pos %u\n",
+			err, cp - name, pos);
+	return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+	return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+		struct se_portal_group *se_tpg,
+		struct config_group *group,
+		const char *name)
+{
+	struct se_node_acl *se_nacl, *se_nacl_new;
+	struct sbp_nacl *nacl;
+	u64 guid = 0;
+	u32 nexus_depth = 1;
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+	if (!se_nacl_new)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+	 * when converting a NodeACL from demo mode -> explict
+	 */
+	se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+			name, nexus_depth);
+	if (IS_ERR(se_nacl)) {
+		sbp_release_fabric_acl(se_tpg, se_nacl_new);
+		return se_nacl;
+	}
+
+	nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	nacl->guid = guid;
+	sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+	return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+	core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+	kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+
+	return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	int ret;
+
+	if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
+		tport->enable = 0;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+		struct se_wwn *wwn,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	struct sbp_tpg *tpg;
+	unsigned long tpgt;
+	int ret;
+
+	if (strstr(name, "tpgt_") != name)
+		return ERR_PTR(-EINVAL);
+	if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+		return ERR_PTR(-EINVAL);
+
+	if (tport->tpg) {
+		pr_err("Only one TPG per Unit is possible.\n");
+		return ERR_PTR(-EBUSY);
+	}
+
+	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+	if (!tpg) {
+		pr_err("Unable to allocate struct sbp_tpg\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tpg->tport = tport;
+	tpg->tport_tpgt = tpgt;
+	tport->tpg = tpg;
+
+	/* default attribute values */
+	tport->enable = 0;
+	tport->directory_id = -1;
+	tport->mgt_orb_timeout = 15;
+	tport->max_reconnect_timeout = 5;
+	tport->max_logins_per_lun = 1;
+
+	tport->mgt_agt = sbp_management_agent_register(tport);
+	if (IS_ERR(tport->mgt_agt)) {
+		ret = PTR_ERR(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+			&tpg->se_tpg, (void *)tpg,
+			TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret < 0) {
+		sbp_management_agent_unregister(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	core_tpg_deregister(se_tpg);
+	sbp_management_agent_unregister(tport->mgt_agt);
+	tport->tpg = NULL;
+	kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+		struct target_fabric_configfs *tf,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport;
+	u64 guid = 0;
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+	if (!tport) {
+		pr_err("Unable to allocate struct sbp_tport\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tport->guid = guid;
+	sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+	return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+		struct target_fabric_configfs *tf,
+		char *page)
+{
+	return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+	&sbp_wwn_version.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_show_directory_id(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	if (tport->directory_id == -1)
+		return sprintf(page, "implicit\n");
+	else
+		return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (tport->enable) {
+		pr_err("Cannot change the directory_id on an active target.\n");
+		return -EBUSY;
+	}
+
+	if (strstr(page, "implicit") == page) {
+		tport->directory_id = -1;
+	} else {
+		if (kstrtoul(page, 16, &val) < 0)
+			return -EINVAL;
+		if (val > 0xffffff)
+			return -EINVAL;
+
+		tport->directory_id = val;
+	}
+
+	return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val != 0) && (val != 1))
+		return -EINVAL;
+
+	if (tport->enable == val)
+		return count;
+
+	if (val) {
+		if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
+			pr_err("Cannot enable a target with no LUNs!\n");
+			return -EINVAL;
+		}
+	} else {
+		/* XXX: force-shutdown sessions instead? */
+		spin_lock(&se_tpg->session_lock);
+		if (!list_empty(&se_tpg->tpg_sess_list)) {
+			spin_unlock(&se_tpg->session_lock);
+			return -EBUSY;
+		}
+		spin_unlock(&se_tpg->session_lock);
+	}
+
+	tport->enable = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0) {
+		pr_err("Could not update Config ROM\n");
+		return ret;
+	}
+
+	return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+	&sbp_tpg_directory_id.attr,
+	&sbp_tpg_enable.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	if (tport->mgt_orb_timeout == val)
+		return count;
+
+	tport->mgt_orb_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 32767))
+		return -EINVAL;
+
+	if (tport->max_reconnect_timeout == val)
+		return count;
+
+	tport->max_reconnect_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	/* XXX: also check against current count? */
+
+	tport->max_logins_per_lun = val;
+
+	return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+	&sbp_tpg_attrib_mgt_orb_timeout.attr,
+	&sbp_tpg_attrib_max_reconnect_timeout.attr,
+	&sbp_tpg_attrib_max_logins_per_lun.attr,
+	NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+	.get_fabric_name		= sbp_get_fabric_name,
+	.get_fabric_proto_ident		= sbp_get_fabric_proto_ident,
+	.tpg_get_wwn			= sbp_get_fabric_wwn,
+	.tpg_get_tag			= sbp_get_tag,
+	.tpg_get_default_depth		= sbp_get_default_depth,
+	.tpg_get_pr_transport_id	= sbp_get_pr_transport_id,
+	.tpg_get_pr_transport_id_len	= sbp_get_pr_transport_id_len,
+	.tpg_parse_pr_out_transport_id	= sbp_parse_pr_out_transport_id,
+	.tpg_check_demo_mode		= sbp_check_true,
+	.tpg_check_demo_mode_cache	= sbp_check_true,
+	.tpg_check_demo_mode_write_protect = sbp_check_false,
+	.tpg_check_prod_mode_write_protect = sbp_check_false,
+	.tpg_alloc_fabric_acl		= sbp_alloc_fabric_acl,
+	.tpg_release_fabric_acl		= sbp_release_fabric_acl,
+	.tpg_get_inst_index		= sbp_tpg_get_inst_index,
+	.release_cmd			= sbp_release_cmd,
+	.shutdown_session		= sbp_shutdown_session,
+	.close_session			= sbp_close_session,
+	.stop_session			= sbp_stop_session,
+	.fall_back_to_erl0		= sbp_reset_nexus,
+	.sess_logged_in			= sbp_sess_logged_in,
+	.sess_get_index			= sbp_sess_get_index,
+	.write_pending			= sbp_write_pending,
+	.write_pending_status		= sbp_write_pending_status,
+	.set_default_node_attributes	= sbp_set_default_node_attrs,
+	.get_task_tag			= sbp_get_task_tag,
+	.get_cmd_state			= sbp_get_cmd_state,
+	.queue_data_in			= sbp_queue_data_in,
+	.queue_status			= sbp_queue_status,
+	.queue_tm_rsp			= sbp_queue_tm_rsp,
+	.get_fabric_sense_len		= sbp_get_fabric_sense_len,
+	.set_fabric_sense_len		= sbp_set_fabric_sense_len,
+	.is_state_remove		= sbp_is_state_remove,
+	.check_stop_free		= sbp_check_stop_free,
+
+	.fabric_make_wwn		= sbp_make_tport,
+	.fabric_drop_wwn		= sbp_drop_tport,
+	.fabric_make_tpg		= sbp_make_tpg,
+	.fabric_drop_tpg		= sbp_drop_tpg,
+	.fabric_post_link		= sbp_post_link_lun,
+	.fabric_pre_unlink		= sbp_pre_unlink_lun,
+	.fabric_make_np			= NULL,
+	.fabric_drop_np			= NULL,
+	.fabric_make_nodeacl		= sbp_make_nodeacl,
+	.fabric_drop_nodeacl		= sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+	if (!fabric) {
+		pr_err("target_fabric_configfs_init() failed\n");
+		return -ENOMEM;
+	}
+
+	fabric->tf_ops = sbp_ops;
+
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 */
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		pr_err("target_fabric_configfs_register() failed for SBP\n");
+		return ret;
+	}
+
+	sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
+	if (!sbp_workqueue) {
+		target_fabric_configfs_deregister(fabric);
+		return -ENOMEM;
+	}
+
+	sbp_fabric_configfs = fabric;
+
+	return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+	if (!sbp_fabric_configfs)
+		return;
+
+	target_fabric_configfs_deregister(sbp_fabric_configfs);
+	destroy_workqueue(sbp_workqueue);
+	sbp_fabric_configfs = NULL;
+};
+
+static int __init sbp_init(void)
+{
+	int ret;
+
+	ret = sbp_register_configfs();
+	if (ret < 0)
+		return ret;
+
+	return 0;
+};
+
+static void sbp_exit(void)
+{
+	sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
+
Signature · 1 line
1.7.9
#54 lore raw

Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

Quoted 45 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_configfs.c new file +42 -0
@@ -0,0 +1,751 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>

drivers/target/sbp/sbp_configfs.c line 52

Quoted 10 lines
drivers/target/sbp/sbp_configfs.c new file +10 -0
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+struct workqueue_struct *sbp_workqueue;

#include <linux/workqueue.h> goes before this.

[...]

drivers/target/sbp/sbp_configfs.c line 714

Quoted 5 lines
drivers/target/sbp/sbp_configfs.c new file +5 -0
+	sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
+	if (!sbp_workqueue) {
+		target_fabric_configfs_deregister(fabric);
+		return -ENOMEM;
+	}

What are your specific requirements that you cannot use one of the system-wide workqueues?

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#55 lore raw

Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

Quoted 50 lines · Stefan Richter
Quoted 49 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_configfs.c new file +46 -0
@@ -0,0 +1,751 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot<[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include<linux/module.h>
+#include<linux/moduleparam.h>
+#include<linux/version.h>
+#include<linux/init.h>
+#include<linux/slab.h>
+#include<linux/kthread.h>
+#include<linux/types.h>
+#include<linux/string.h>
+#include<linux/configfs.h>
+#include<linux/ctype.h>
+#include<linux/firewire.h>
+
+#include<asm/unaligned.h>
+
+#include<target/target_core_base.h>
+#include<target/target_core_backend.h>
+#include<target/target_core_fabric.h>
+#include<target/target_core_configfs.h>
+#include<target/target_core_fabric_configfs.h>
+#include<target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
Quoted 8 lines

drivers/target/sbp/sbp_configfs.c line 52

Quoted 6 lines
drivers/target/sbp/sbp_configfs.c new file +6 -0
+#include "sbp_management_agent.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+struct workqueue_struct *sbp_workqueue;

#include<linux/workqueue.h> goes before this.

Yep.

Quoted 9 lines

[...]

drivers/target/sbp/sbp_configfs.c line 714

Quoted 5 lines
drivers/target/sbp/sbp_configfs.c new file +5 -0
+	sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
+	if (!sbp_workqueue) {
+		target_fabric_configfs_deregister(fabric);
+		return -ENOMEM;
+	}

What are your specific requirements that you cannot use one of the system-wide workqueues?

Nothing specific, I just thought it was sensible to use your own workqueue if you put enough work into it. I'll switch to the system queues.

Signature · 2 lines
Chris Boot
[email protected]
#56 lore raw

Re: [PATCH v2 05/11] firewire-sbp-target: Add sbp_configfs.c

Quoted 11 lines · On Feb 16 Chris Boot wrote:
Quoted 9 lines · Stefan Richter

drivers/target/sbp/sbp_configfs.c line 714

Quoted 5 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_configfs.c new file +5 -0
+	sbp_workqueue = alloc_workqueue("firewire-sbp-target", WQ_UNBOUND, 0);
+	if (!sbp_workqueue) {
+		target_fabric_configfs_deregister(fabric);
+		return -ENOMEM;
+	}

What are your specific requirements that you cannot use one of the system-wide workqueues?

Quoted 2 lines

Nothing specific, I just thought it was sensible to use your own workqueue if you put enough work into it. I'll switch to the system queues.

OK, good. These days you can throw almost any kind of work into the system workqueues without negative effect on their other users, given you keep the queue properties in mind which are listed in linux/workqueue.h. There is also some info in Documentation/workqueue.txt. If that still leaves any doubt, you could Cc: Tejun Hejo on questions on the workqueue infrastructure and he will likely give a helpful hint.

BTW, the drivers/firewire/ subsystem uses an own workqueue instead of the system-wide ones because of combined requirements of non-reentrance and memory-reclaim safety, explained in the changelog of commit 6ea9e7bbfc38.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- =----
http://arcgraph.de/sr/
author maintainer
#57 lore raw

PATCH v2 6/11 firewire-sbp-target: Add sbp_fabric.{c,h}

In reply to Chris Boot

This serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_fabric.c |  353 +++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_fabric.h |   42 +++++
 2 files changed, 395 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_fabric.c
 create mode 100644 drivers/target/sbp/sbp_fabric.h
drivers/target/sbp/sbp_fabric.c new file +353 -0
new file mode 100644
index 0000000..354ef93
@@ -0,0 +1,353 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+int sbp_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+int sbp_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+char *sbp_get_fabric_name(void)
+{
+	return "sbp";
+}
+
+char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	return &tport->tport_name[0];
+}
+
+u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	return tpg->tport_tpgt;
+}
+
+u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+	struct sbp_nacl *nacl;
+
+	nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+	if (!nacl) {
+		pr_err("Unable to alocate struct sbp_nacl\n");
+		return NULL;
+	}
+
+	return &nacl->se_node_acl;
+}
+
+void sbp_release_fabric_acl(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	kfree(nacl);
+}
+
+u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	sbp_free_request(req);
+}
+
+int sbp_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+void sbp_close_session(struct se_session *se_sess)
+{
+	return;
+}
+
+void sbp_stop_session(struct se_session *se_sess, int sess_sleep,
+		int conn_sleep)
+{
+	return;
+}
+
+void sbp_reset_nexus(struct se_session *se_sess)
+{
+	return;
+}
+
+int sbp_sess_logged_in(struct se_session *se_sess)
+{
+	return 0;
+}
+
+u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+int sbp_write_pending(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+	if (!req->data_buf)
+		return -ENOMEM;
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		pr_warn("sbp_write_pending: data write error\n");
+		return ret;
+	}
+
+	sg_copy_from_buffer(se_cmd->t_data_sg,
+			se_cmd->t_data_nents,
+			req->data_buf,
+			se_cmd->data_length);
+
+	transport_generic_process_write(se_cmd);
+
+	return 0;
+}
+
+int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+	return;
+}
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	/* only used for printk until we do TMRs */
+	return (u32)req->orb_pointer;
+}
+
+int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	req->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+	if (!req->data_buf)
+		return -ENOMEM;
+
+	sg_copy_to_buffer(se_cmd->t_data_sg,
+		se_cmd->t_data_nents,
+		req->data_buf,
+		se_cmd->data_length);
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	return sbp_send_sense(req);
+}
+
+int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+	return 0;
+}
+
+u16 sbp_get_fabric_sense_len(void)
+{
+	return 0;
+}
+
+int sbp_is_state_remove(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	transport_generic_free_cmd(&req->se_cmd, 0);
+	return 1;
+}
+
+/*
+ * Handlers for Serial Bus Protocol 2/3 (SBP-2 / SBP-3)
+ */
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+	/*
+	 * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+	 * This is defined in section 7.5.1 Table 362 in spc4r17
+	 */
+	return SCSI_PROTOCOL_SBP;
+}
+
+u32 sbp_get_pr_transport_id(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code,
+	unsigned char *buf)
+{
+	int ret;
+
+	/*
+	 * Set PROTOCOL IDENTIFIER to 3h for SBP
+	 */
+	buf[0] = SCSI_PROTOCOL_SBP;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 */
+	ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+	if (ret < 0)
+		pr_debug("sbp transport_id: invalid hex string\n");
+
+	/*
+	 * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+
+u32 sbp_get_pr_transport_id_len(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code)
+{
+	*format_code = 0;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 *
+	 * The SBP Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+char *sbp_parse_pr_out_transport_id(
+	struct se_portal_group *se_tpg,
+	const char *buf,
+	u32 *out_tid_len,
+	char **port_nexus_ptr)
+{
+	/*
+	 * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+	 * for initiator ports using SCSI over SBP Serial SCSI Protocol
+	 *
+	 * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+	 * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+	 * so we return the **port_nexus_ptr set to NULL.
+	 */
+	*port_nexus_ptr = NULL;
+	*out_tid_len = 24;
+
+	return (char *)&buf[8];
+}
+
drivers/target/sbp/sbp_fabric.h new file +42 -0

42 lines not on this page · raw message

Signature · 1 line
1.7.9
author maintainer
#58 lore raw

PATCH v2 7/11 firewire-sbp-target: add sbp_management_agent.{c,h}

In reply to Chris Boot

This code implements the SBP-2 Management Agent. This is the first of two firewire address handlers that are used to communicate with the target. The Management Agent is used to handle login, reconnect and logout to a SCSI LUN as well as task management functions.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_management_agent.c |  260 +++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_management_agent.h |   23 +++
 2 files changed, 283 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_management_agent.c
 create mode 100644 drivers/target/sbp/sbp_management_agent.h
drivers/target/sbp/sbp_management_agent.c new file +260 -0
new file mode 100644
index 0000000..04e4df8
@@ -0,0 +1,260 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kref.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+	struct sbp_management_agent *agent =
+		container_of(work, struct sbp_management_agent, work);
+	struct sbp_management_request *req = agent->request;
+	int ret;
+	int status_data_len = 0;
+
+	/* fetch the ORB from the initiator */
+	ret = fw_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		agent->orb_offset, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb fetch failed: %x\n", ret);
+		goto out;
+	}
+
+	pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
+		sbp2_pointer_to_addr(&req->orb.ptr1),
+		sbp2_pointer_to_addr(&req->orb.ptr2),
+		be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+		sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+	if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+		ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+		pr_err("mgt_orb bad request\n");
+		goto out;
+	}
+
+	switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+	case MANAGEMENT_ORB_FUNCTION_LOGIN:
+		sbp_management_request_login(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+		sbp_management_request_query_logins(agent, req,
+				&status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+		sbp_management_request_reconnect(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+		pr_notice("SET PASSWORD not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+		sbp_management_request_logout(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+		pr_notice("ABORT TASK not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+		pr_notice("ABORT TASK SET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+		pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+		pr_notice("TARGET RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	default:
+		pr_notice("unknown management function 0x%x\n",
+			MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+	}
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+		STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+		STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+	/* write the status block back to the initiator */
+	ret = fw_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		sbp2_pointer_to_addr(&req->orb.status_fifo),
+		&req->status, 8 + status_data_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb status write failed: %x\n", ret);
+		goto out;
+	}
+
+out:
+	fw_card_put(req->card);
+	kfree(req);
+	atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_management_agent *agent = callback_data;
+	struct sbp2_pointer *ptr = data;
+
+	if (!agent->tport->enable) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if ((offset != agent->handler.offset) || (length != 8)) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		struct sbp_management_request *req;
+		int ret;
+
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					MANAGEMENT_AGENT_STATE_IDLE,
+					MANAGEMENT_AGENT_STATE_BUSY) !=
+				MANAGEMENT_AGENT_STATE_IDLE) {
+			pr_notice("ignoring management request while busy\n");
+
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}
+
+		req = kzalloc(sizeof(*req), GFP_ATOMIC);
+		if (!req) {
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}
+
+		req->card = fw_card_get(card);
+		req->generation = generation;
+		req->node_addr = source;
+		req->speed = fw_get_request_speed(request);
+
+		agent->orb_offset = sbp2_pointer_to_addr(ptr);
+		agent->request = req;
+
+		ret = queue_work(sbp_workqueue, &agent->work);
+		if (!ret) {
+			/* pretend we're busy */
+			kfree(req);
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}
+
+		fw_send_response(card, request, RCODE_COMPLETE);
+	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+		addr_to_sbp2_pointer(agent->orb_offset, ptr);
+		fw_send_response(card, request, RCODE_COMPLETE);
+	} else {
+		fw_send_response(card, request, RCODE_TYPE_ERROR);
+	}
+}
+
+struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport)
+{
+	int ret;
+	struct sbp_management_agent *agent;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	agent->tport = tport;
+	agent->handler.length = 0x08;
+	agent->handler.address_callback = sbp_mgt_agent_rw;
+	agent->handler.callback_data = agent;
+	atomic_set(&agent->state, MANAGEMENT_AGENT_STATE_IDLE);
+	INIT_WORK(&agent->work, sbp_mgt_agent_process);
+	agent->orb_offset = 0;
+	agent->request = NULL;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+			&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+	if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}
+
drivers/target/sbp/sbp_management_agent.h new file +23 -0
new file mode 100644
index 0000000..615b90e
@@ -0,0 +1,23 @@
+
+struct sbp_management_agent {
+	struct sbp_tport *tport;
+	struct fw_address_handler handler;
+	atomic_t state;
+	struct work_struct work;
+	u64 orb_offset;
+	struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+	struct sbp_management_orb orb;
+	struct sbp_status_block status;
+	struct fw_card *card;
+	int generation;
+	int node_addr;
+	int speed;
+};
+
+struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport);
+void sbp_management_agent_unregister(struct sbp_management_agent *agent);
+
Signature · 1 line
1.7.9
#59 lore raw

Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

Quoted 2 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_management_agent.c new file +0 -0

[...]

Quoted 22 lines
drivers/target/sbp/sbp_management_agent.c new file +22 -0
+static void sbp_mgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_management_agent *agent = callback_data;
+	struct sbp2_pointer *ptr = data;
+
+	if (!agent->tport->enable) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if ((offset != agent->handler.offset) || (length != 8)) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		struct sbp_management_request *req;
+		int ret;
+

drivers/target/sbp/sbp_management_agent.c line 190

Quoted 10 lines
drivers/target/sbp/sbp_management_agent.c new file +10 -0
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					MANAGEMENT_AGENT_STATE_IDLE,
+					MANAGEMENT_AGENT_STATE_BUSY) !=
+				MANAGEMENT_AGENT_STATE_IDLE) {
+			pr_notice("ignoring management request while busy\n");
+
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}

There is a rule of thumb which says: If you add a memory barrier anywhere in your code, also add a comment saying which accesses this barrier is meant to bring into order.

So after the write barrier is apparently the agent->state access. What access is before the barrier?

And how does the read side look like?

These questions are mostly rhetoric. It is quite likely that this code is better off with a plain and simple mutex serialization.

[...]

drivers/target/sbp/sbp_management_agent.c line 259

Quoted 8 lines
drivers/target/sbp/sbp_management_agent.c new file +8 -0
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+	if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}

I still have yet to test-apply all your patches, look at the sum of the code and understand what the execution contexts and critical sections are. So I really should not yet ask the next, uninformed question.

Looking at this function, I wonder: Can the agent->state change after you read it, and what would happen then?

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#60 lore raw

Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

Quoted 41 lines · Stefan Richter
Quoted 2 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_management_agent.c new file +0 -0

[...]

Quoted 22 lines
drivers/target/sbp/sbp_management_agent.c new file +22 -0
+static void sbp_mgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_management_agent *agent = callback_data;
+	struct sbp2_pointer *ptr = data;
+
+	if (!agent->tport->enable) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if ((offset != agent->handler.offset) || (length != 8)) {
+		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+		return;
+	}
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		struct sbp_management_request *req;
+		int ret;
+

drivers/target/sbp/sbp_management_agent.c line 190

Quoted 10 lines
drivers/target/sbp/sbp_management_agent.c new file +10 -0
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					MANAGEMENT_AGENT_STATE_IDLE,
+					MANAGEMENT_AGENT_STATE_BUSY) !=
+				MANAGEMENT_AGENT_STATE_IDLE) {
+			pr_notice("ignoring management request while busy\n");
+
+			fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+			return;
+		}

There is a rule of thumb which says: If you add a memory barrier anywhere in your code, also add a comment saying which accesses this barrier is meant to bring into order.

Quoted 7 lines

So after the write barrier is apparently the agent->state access. What access is before the barrier?

And how does the read side look like?

These questions are mostly rhetoric. It is quite likely that this code is better off with a plain and simple mutex serialization.

Well as I mentioned in my previous messages atomics and locking are pretty new ideas to me. Having only really done threading in PERL before, the world of doing it in the kernel is rather different! :-) The memory barriers are there after I looked in Documentation/atomic_ops.txt and clearly misunderstood the entire thing.

Reading about mutexes though I see I can't use them from interrupt context, but doesn't the FireWire address handler execute in interrupt context? I have to check the state of the management agent in the address handler to properly reject requests from the initiator when the agent is busy. I guess a spinlock is called for in this situation, possibly using spin_trylock() in the address handler to avoid blocking?

Quoted 14 lines

[...]

drivers/target/sbp/sbp_management_agent.c line 259

Quoted 8 lines
drivers/target/sbp/sbp_management_agent.c new file +8 -0
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+	if (atomic_read(&agent->state) != MANAGEMENT_AGENT_STATE_IDLE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}

I still have yet to test-apply all your patches, look at the sum of the code and understand what the execution contexts and critical sections are. So I really should not yet ask the next, uninformed question.

Quoted 2 lines

Looking at this function, I wonder: Can the agent->state change after you read it, and what would happen then?

The agent state could change between the read and before the address handler is unregistered, this would require an initiator to keep sending management requests long after the FW unit descriptor is removed though.

I guess to solve this in a bulletproof manner I need a kref in struct sbp_management_agent and a spinlock(?), and a new state to say the agent is going away. That way if I happen to receive requests while the agent is being removed it can reply with address errors before the address handler is fully removed. I'd also need something similar in the target agent.

Signature · 2 lines
Chris Boot
[email protected]
#61 lore raw

Re: [PATCH v2 07/11] firewire-sbp-target: add sbp_management_agent.{c,h}

Quoted 6 lines · On Feb 16 Chris Boot wrote:

Reading about mutexes though I see I can't use them from interrupt context, but doesn't the FireWire address handler execute in interrupt context? I have to check the state of the management agent in the address handler to properly reject requests from the initiator when the agent is busy. I guess a spinlock is called for in this situation, possibly using spin_trylock() in the address handler to avoid blocking?

Yes; the request-receive callback (address handler) is executed in tasklet context. All drivers which use this currently use the spin_lock_irqsave API variant. I am considering to change the entire subsystem to use spin_lock_bh only. IMO you could use spin_lock_bh in your target code, or spin_lock for locks which are _only_ ever taken in tasklet context.

Plain spin_lock cannot be used, neither in the address handler nor in any process context which grabs the same lock as the address handler.

The use cases for trylock variants of the locking APIs are somewhat special. If in doubt, ignore that trylock APIs exist.

In strict terms, spin_trylock avoids busy-waiting for a contended lock. mutex_trylock avoids blocking in the sense of sleeping wait for a contended mutex. However, to expand on the obvious, the trylock variants are only useful if whatever action which was meant to be performed can be aborted without problem, e.g. if upper layers would be able to retry at some later occasion.

(I am still interested in closer looks at the execution contexts and object lifetime rules in your code, but spare time vs. mental capacity seem regularly at odds...)

Signature · 3 lines
Stefan Richter
-=====-===-- --=- =----
http://arcgraph.de/sr/
author maintainer
#62 lore raw

PATCH v2 8/11 firewire-sbp-target: Add sbp_login.{c,h}

In reply to Chris Boot

This file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_login.c |  665 ++++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_login.h |   14 +
 2 files changed, 679 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_login.c
 create mode 100644 drivers/target/sbp/sbp_login.h
drivers/target/sbp/sbp_login.c new file +665 -0
new file mode 100644
index 0000000..74b5eaf
@@ -0,0 +1,665 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+	int ret;
+	__be32 high, low;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+			&high, sizeof(high));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+			&low, sizeof(low));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	*guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+	return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+	struct sbp_tpg *tpg, u64 guid)
+{
+	struct se_session *se_sess;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		if (sess->guid == guid) {
+			spin_unlock(&tpg->se_tpg.session_lock);
+			return sess;
+		}
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return NULL;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+		struct sbp_session *session, struct se_lun *lun)
+{
+	struct sbp_login_descriptor *login;
+
+	spin_lock(&session->login_list_lock);
+	list_for_each_entry(login, &session->login_list, link) {
+		if (login->lun == lun) {
+			spin_unlock(&session->login_list_lock);
+			return login;
+		}
+	}
+	spin_unlock(&session->login_list_lock);
+
+	return NULL;
+}
+
+static int sbp_login_count_all_by_lun(
+		struct sbp_tpg *tpg,
+		struct se_lun *lun,
+		int exclusive)
+{
+	struct se_session *se_sess;
+	int count = 0;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		struct sbp_login_descriptor *login;
+
+		spin_lock(&sess->login_list_lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->lun != lun)
+				continue;
+
+			if (!exclusive) {
+				count++;
+				continue;
+			}
+
+			if (login->exclusive)
+				count++;
+		}
+		spin_unlock(&sess->login_list_lock);
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+	struct sbp_tpg *tpg, int login_id)
+{
+	struct se_session *se_sess;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		struct sbp_login_descriptor *login;
+
+		spin_lock(&sess->login_list_lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->login_id == login_id) {
+				spin_unlock(&sess->login_list_lock);
+				spin_unlock(&tpg->se_tpg.session_lock);
+				return login;
+			}
+		}
+		spin_unlock(&sess->login_list_lock);
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return NULL;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+	struct se_portal_group *se_tpg = &tpg->se_tpg;
+	struct se_lun *se_lun;
+
+	if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+		return ERR_PTR(-ENODEV);
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+	se_lun = &se_tpg->tpg_lun_list[lun];
+
+	if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+		se_lun = ERR_PTR(-EINVAL);
+
+	spin_unlock(&se_tpg->tpg_lun_lock);
+
+	return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+		struct sbp_tpg *tpg,
+		u64 guid)
+{
+	struct sbp_session *sess;
+	int ret;
+	char guid_str[17];
+	struct se_node_acl *se_nacl;
+
+	sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+	if (!sess) {
+		pr_err("failed to allocate session descriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sess->se_sess = transport_init_session();
+	if (IS_ERR(sess->se_sess)) {
+		pr_err("failed to init se_session\n");
+
+		ret = PTR_ERR(sess->se_sess);
+		kfree(sess);
+		return ERR_PTR(ret);
+	}
+
+	snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+	se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+	if (!se_nacl) {
+		pr_warn("Node ACL not found for %s\n", guid_str);
+
+		transport_free_session(sess->se_sess);
+		kfree(sess);
+
+		return ERR_PTR(-EPERM);
+	}
+
+	sess->se_sess->se_node_acl = se_nacl;
+
+	INIT_LIST_HEAD(&sess->login_list);
+	spin_lock_init(&sess->login_list_lock);
+	INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+
+	sess->guid = guid;
+
+	transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+	return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+	spin_lock(&sess->login_list_lock);
+	if (!list_empty(&sess->login_list)) {
+		spin_unlock(&sess->login_list_lock);
+		return;
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	transport_deregister_session_configfs(sess->se_sess);
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->card)
+		fw_card_put(sess->card);
+
+	if (cancel_work)
+		cancel_delayed_work_sync(&sess->maint_work);
+
+	kfree(sess);
+}
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+	bool cancel_work)
+{
+	struct sbp_session *sess = login->sess;
+
+	/* FIXME: abort/wait on tasks */
+
+	spin_lock(&sess->login_list_lock);
+	list_del(&login->link);
+	spin_unlock(&sess->login_list_lock);
+
+	sbp_target_agent_unregister(login->tgt_agt);
+	sbp_session_release(sess, cancel_work);
+	kfree(login);
+}
+
+void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	struct se_lun *se_lun;
+	int ret;
+	u64 guid;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login;
+	struct sbp_login_response_block *response;
+	int login_response_len;
+
+	se_lun = sbp_get_lun_from_tpg(tpg,
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+	if (IS_ERR(se_lun)) {
+		pr_notice("login to unknown LUN: %d\n",
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+		return;
+	}
+
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+		se_lun->unpacked_lun, guid);
+
+	sess = sbp_session_find_by_guid(tpg, guid);
+	if (sess) {
+		login = sbp_login_find_by_lun(sess, se_lun);
+		if (login) {
+			pr_notice("initiator already logged-in\n");
+
+			/*
+			 * SBP-2 R4 says we should return access denied, but
+			 * that can confuse initiators. Instead we need to
+			 * treat this like a reconnect, but send the login
+			 * response block like a fresh login.
+			 *
+			 * This is required particularly in the case of Apple
+			 * devices booting off the FireWire target, where
+			 * the firmware has an active login to the target. When
+			 * the OS takes control of the session it issues its own
+			 * LOGIN rather than a RECONNECT. To avoid the machine
+			 * waiting until the reconnect_hold expires, we can skip
+			 * the ACCESS_DENIED errors to speed things up.
+			 */
+
+			goto already_logged_in;
+		}
+	}
+
+	/*
+	 * check exclusive bit in login request
+	 * reject with access_denied if any logins present
+	 */
+	if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+			sbp_login_count_all_by_lun(tpg, se_lun, 0)) {
+		pr_warn("refusing exclusive login with other active logins\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check exclusive bit in any existing login descriptor
+	 * reject with access_denied if any exclusive logins present
+	 */
+	if (sbp_login_count_all_by_lun(tpg, se_lun, 1)) {
+		pr_warn("refusing login while another exclusive login present\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check we haven't exceeded the number of allowed logins
+	 * reject with resources_unavailable if we have
+	 */
+	if (sbp_login_count_all_by_lun(tpg, se_lun, 0) >=
+			tport->max_logins_per_lun) {
+		pr_warn("max number of logins reached\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	if (!sess) {
+		sess = sbp_session_create(tpg, guid);
+		if (IS_ERR(sess)) {
+			switch (PTR_ERR(sess)) {
+			case -EPERM:
+				ret = SBP_STATUS_ACCESS_DENIED;
+				break;
+			default:
+				ret = SBP_STATUS_RESOURCES_UNAVAIL;
+				break;
+			}
+
+			req->status.status = cpu_to_be32(
+				STATUS_BLOCK_RESP(
+					STATUS_RESP_REQUEST_COMPLETE) |
+				STATUS_BLOCK_SBP_STATUS(ret));
+			return;
+		}
+
+		sess->node_id = req->node_addr;
+		sess->card = fw_card_get(req->card);
+		sess->generation = req->generation;
+		sess->speed = req->speed;
+
+		queue_delayed_work(sbp_workqueue, &sess->maint_work,
+			SESSION_MAINTENANCE_INTERVAL);
+	}
+
+	/* only take the latest reconnect_hold into account */
+	sess->reconnect_hold = min(
+		1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+		tport->max_reconnect_timeout) - 1;
+
+	login = kmalloc(sizeof(*login), GFP_KERNEL);
+	if (!login) {
+		pr_err("failed to allocate login descriptor\n");
+
+		sbp_session_release(sess, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login->sess = sess;
+	login->lun = se_lun;
+	login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+	login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+	login->login_id = atomic_inc_return(&login_id);
+	atomic_set(&login->unsolicited_status_enable, 0);
+
+	login->tgt_agt = sbp_target_agent_register(login);
+	if (IS_ERR(login->tgt_agt)) {
+		ret = PTR_ERR(login->tgt_agt);
+		pr_err("failed to map command block handler: %d\n", ret);
+
+		sbp_session_release(sess, true);
+		kfree(login);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	spin_lock(&sess->login_list_lock);
+	list_add_tail(&login->link, &sess->login_list);
+	spin_unlock(&sess->login_list_lock);
+
+already_logged_in:
+	response = kzalloc(sizeof(*response), GFP_KERNEL);
+	if (!response) {
+		pr_err("failed to allocate login response block\n");
+
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login_response_len = clamp_val(
+			LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
+			12, sizeof(*response));
+	response->misc = cpu_to_be32(
+		((login_response_len & 0xffff) << 16) |
+		(login->login_id & 0xffff));
+	response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+	addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+		&response->command_block_agent);
+
+	ret = fw_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		sbp2_pointer_to_addr(&req->orb.ptr2), response,
+		login_response_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("failed to write login response block: %x\n", ret);
+
+		kfree(response);
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	kfree(response);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	pr_notice("QUERY LOGINS not implemented\n");
+	/* FIXME: implement */
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int ret;
+	u64 guid;
+	struct sbp_login_descriptor *login;
+
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+	login = sbp_login_find_by_id(tpg,
+		RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+	if (!login) {
+		pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	if (login->sess->guid != guid) {
+		pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	if (login->sess->card)
+		fw_card_put(login->sess->card);
+
+	/* update the node details */
+	login->sess->generation = req->generation;
+	login->sess->node_id = req->node_addr;
+	login->sess->card = fw_card_get(req->card);
+	login->sess->speed = req->speed;
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int login_id;
+	struct sbp_login_descriptor *login;
+
+	login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+	login = sbp_login_find_by_id(tpg, login_id);
+	if (!login) {
+		pr_warn("cannot find login: %d\n", login_id);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+		return;
+	}
+
+	pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+		login->lun->unpacked_lun, login->login_id);
+
+	if (req->node_addr != login->sess->node_id) {
+		pr_warn("logout from different node ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	sbp_login_release(login, true);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {
+		pr_info("Waiting for reconnect from node: %016llx\n",
+			sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+	struct sbp_login_descriptor *login, *temp;
+
+	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+	spin_lock(&sess->login_list_lock);
+	list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+		spin_unlock(&sess->login_list_lock);
+		sbp_login_release(login, false);
+		spin_lock(&sess->login_list_lock);
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	/* sbp_login_release() calls sbp_session_release() */
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+	struct sbp_session *sess = container_of(work, struct sbp_session,
+		maint_work.work);
+
+	/* could be called while tearing down the session */
+	spin_lock(&sess->login_list_lock);
+	if (list_empty(&sess->login_list)) {
+		spin_unlock(&sess->login_list_lock);
+		return;
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	if (sess->node_id != -1) {
+		/* check for bus reset and make node_id invalid */
+		session_check_for_reset(sess);
+
+		queue_delayed_work(sbp_workqueue, &sess->maint_work,
+			SESSION_MAINTENANCE_INTERVAL);
+	} else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+		/* still waiting for reconnect */
+		queue_delayed_work(sbp_workqueue, &sess->maint_work,
+			SESSION_MAINTENANCE_INTERVAL);
+	} else {
+		/* reconnect timeout has expired */
+		session_reconnect_expired(sess);
+	}
+}
+
drivers/target/sbp/sbp_login.h new file +14 -0
new file mode 100644
index 0000000..4c86dcb
@@ -0,0 +1,14 @@
+
+extern void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+
Signature · 1 line
1.7.9
#63 lore raw

Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

Quoted 94 lines · On Feb 15 Chris Boot wrote:

This file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_login.c |  665 ++++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_login.h |   14 +
 2 files changed, 679 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_login.c
 create mode 100644 drivers/target/sbp/sbp_login.h
drivers/target/sbp/sbp_login.c new file +73 -0
new file mode 100644
index 0000000..74b5eaf
@@ -0,0 +1,665 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+	int ret;
+	__be32 high, low;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+			&high, sizeof(high));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+			&low, sizeof(low));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	*guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+	return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+	struct sbp_tpg *tpg, u64 guid)
+{
+	struct se_session *se_sess;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {

drivers/target/sbp/sbp_login.c line 83

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		if (sess->guid == guid) {
+			spin_unlock(&tpg->se_tpg.session_lock);
+			return sess;
+		}
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return NULL;
+}

Another form to write this would be

static struct sbp_session *sbp_session_find_by_guid(
struct sbp_tpg *tpg, u64 guid)

{
	struct se_session *se_sess;
	struct sbp_session *s, *session = NULL;

	spin_lock(&tpg->se_tpg.session_lock);
	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
		s = se_sess->fabric_sess_ptr;
		if (s->guid == guid) {
			session = s;
			break;
		}
	}
	spin_unlock(&tpg->se_tpg.session_lock);

	return session;
}

But since your function is very small, the dual unlock-and-exit paths are not a problem for readability.

As an aside, here is a variation of the theme, though weirdly looking if one never came across it before:

static struct sbp_session *sbp_session_find_by_guid(
struct sbp_tpg *tpg, u64 guid)

{
	struct se_session *s;

	spin_lock(&tpg->se_tpg.session_lock);
	list_for_each_entry(s, &tpg->se_tpg.tpg_sess_list, sess_list)
		if (s->fabric_sess_ptr->guid == guid)
			break;
	spin_unlock(&tpg->se_tpg.session_lock);

	if (&s->sess_list != &tpg->se_tpg.tpg_sess_list)
		return s->fabric_sess_ptr;
	else
		return NULL;
}

[...]

Quoted 14 lines
drivers/target/sbp/sbp_login.c new file +14 -0
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+	struct sbp_tpg *tpg, int login_id)
+{
+	struct se_session *se_sess;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		struct sbp_login_descriptor *login;
+
+		spin_lock(&sess->login_list_lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->login_id == login_id) {
+				spin_unlock(&sess->login_list_lock);

drivers/target/sbp/sbp_login.c line 158

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+				spin_unlock(&tpg->se_tpg.session_lock);
+				return login;
+			}
+		}
+		spin_unlock(&sess->login_list_lock);
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return NULL;
+}

This function on the other hand might indeed benefit from a style involving a single unlock-and-exit path.

[...]

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+	spin_lock(&sess->login_list_lock);
+	if (!list_empty(&sess->login_list)) {
+		spin_unlock(&sess->login_list_lock);
+		return;
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	transport_deregister_session_configfs(sess->se_sess);

drivers/target/sbp/sbp_login.c line 247

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->card)
+		fw_card_put(sess->card);
+
+	if (cancel_work)
+		cancel_delayed_work_sync(&sess->maint_work);
+
+	kfree(sess);
+}

What prevents that an entry is added to sess->login_list right after you tested for it being empty?

If there is something external which prevents this, then you don't need to take the lock just for this test.

If there is no such external measure of serialization, then the spinlock-protected section is too small.

By the way, the use of spin_lock()/spin_unlock() is quite atypical. This API restricts you
- not to call a possibly sleeping function within the lock- protected section,
- not to take the lock in tasklet context or IRQ context.

So this locking API is quite rarely used: Anywhere where a mutex /could/ be used, but none of the locked sections ever need to sleep. This is a rather narrow use case.

Maybe you know all this but I thought I mention it anyway.

Quoted 14 lines
drivers/target/sbp/sbp_login.c new file +14 -0
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}

drivers/target/sbp/sbp_login.c line 618

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {
+		pr_info("Waiting for reconnect from node: %016llx\n",
+			sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+}

[Note to self: When more awake, carefully review this peeking into fw_card internals, the generation accesses, and the card refcounting.]

Quoted 6 lines
drivers/target/sbp/sbp_login.c new file +6 -0
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+	struct sbp_login_descriptor *login, *temp;
+
+	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+

drivers/target/sbp/sbp_login.c line 635

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+	spin_lock(&sess->login_list_lock);
+	list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+		spin_unlock(&sess->login_list_lock);
+		sbp_login_release(login, false);
+		spin_lock(&sess->login_list_lock);
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	/* sbp_login_release() calls sbp_session_release() */
+}

This is wrong. Either something external protects the session_reconnect_expired() executing context from concurrent manipulations of sess->login_list. Then you don't need to take the lock here in the first place.

Or there is no such external serialization measure. Then you must not drop the list lock in the loop body.

In the latter case, an easy fix would be to move the expired logins to a local temporary list while holding the lock, then release each item from the temporary list without holding the lock.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#64 lore raw

Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

Quoted 204 lines · Stefan Richter
Quoted 94 lines · On Feb 15 Chris Boot wrote:

This file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.

Cc Andy Grover<[email protected]>
Cc Clemens Ladisch<[email protected]>
Cc Nicholas A. Bellinger<[email protected]>
Cc Stefan Richter<[email protected]>
---
  drivers/target/sbp/sbp_login.c |  665 ++++++++++++++++++++++++++++++++++++++++
  drivers/target/sbp/sbp_login.h |   14 +
  2 files changed, 679 insertions(+), 0 deletions(-)

create mode 100644 drivers/target/sbp/sbp_login.c create mode 100644 drivers/target/sbp/sbp_login.h

drivers/target/sbp/sbp_login.c new file +73 -0
new file mode 100644
index 0000000..74b5eaf
@@ -0,0 +1,665 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot<[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include<linux/kref.h>
+#include<linux/firewire.h>
+#include<linux/firewire-constants.h>
+#include<linux/slab.h>
+
+#include<target/target_core_base.h>
+#include<target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+	int ret;
+	__be32 high, low;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+			&high, sizeof(high));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	ret = fw_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+			&low, sizeof(low));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	*guid = (u64)be32_to_cpu(high)<<  32 | be32_to_cpu(low);
+
+	return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+	struct sbp_tpg *tpg, u64 guid)
+{
+	struct se_session *se_sess;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess,&tpg->se_tpg.tpg_sess_list, sess_list) {

drivers/target/sbp/sbp_login.c line 83

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		if (sess->guid == guid) {
+			spin_unlock(&tpg->se_tpg.session_lock);
+			return sess;
+		}
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return NULL;
+}

Another form to write this would be

static struct sbp_session *sbp_session_find_by_guid(
struct sbp_tpg *tpg, u64 guid)

{
	struct se_session *se_sess;
	struct sbp_session *s, *session = NULL;

	spin_lock(&tpg->se_tpg.session_lock);
	list_for_each_entry(se_sess,&tpg->se_tpg.tpg_sess_list, sess_list) {
		s = se_sess->fabric_sess_ptr;
		if (s->guid == guid) {
			session = s;
			break;
		}
	}
	spin_unlock(&tpg->se_tpg.session_lock);

	return session;
}

But since your function is very small, the dual unlock-and-exit paths are not a problem for readability.

As an aside, here is a variation of the theme, though weirdly looking if one never came across it before:

static struct sbp_session *sbp_session_find_by_guid(
struct sbp_tpg *tpg, u64 guid)

{
	struct se_session *s;

	spin_lock(&tpg->se_tpg.session_lock);
	list_for_each_entry(s,&tpg->se_tpg.tpg_sess_list, sess_list)
		if (s->fabric_sess_ptr->guid == guid)
			break;
	spin_unlock(&tpg->se_tpg.session_lock);

	if (&s->sess_list !=&tpg->se_tpg.tpg_sess_list)
		return s->fabric_sess_ptr;
	else
		return NULL;
}

[...]

Quoted 14 lines
drivers/target/sbp/sbp_login.c new file +14 -0
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+	struct sbp_tpg *tpg, int login_id)
+{
+	struct se_session *se_sess;
+
+	spin_lock(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess,&tpg->se_tpg.tpg_sess_list, sess_list) {
+		struct sbp_session *sess = se_sess->fabric_sess_ptr;
+		struct sbp_login_descriptor *login;
+
+		spin_lock(&sess->login_list_lock);
+		list_for_each_entry(login,&sess->login_list, link) {
+			if (login->login_id == login_id) {
+				spin_unlock(&sess->login_list_lock);

drivers/target/sbp/sbp_login.c line 158

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+				spin_unlock(&tpg->se_tpg.session_lock);
+				return login;
+			}
+		}
+		spin_unlock(&sess->login_list_lock);
+	}
+	spin_unlock(&tpg->se_tpg.session_lock);
+
+	return NULL;
+}

This function on the other hand might indeed benefit from a style involving a single unlock-and-exit path.

[...]

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+	spin_lock(&sess->login_list_lock);
+	if (!list_empty(&sess->login_list)) {
+		spin_unlock(&sess->login_list_lock);
+		return;
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	transport_deregister_session_configfs(sess->se_sess);

drivers/target/sbp/sbp_login.c line 247

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->card)
+		fw_card_put(sess->card);
+
+	if (cancel_work)
+		cancel_delayed_work_sync(&sess->maint_work);
+
+	kfree(sess);
+}

What prevents that an entry is added to sess->login_list right after you tested for it being empty?

Quoted 5 lines

If there is something external which prevents this, then you don't need to take the lock just for this test.

If there is no such external measure of serialization, then the spinlock-protected section is too small.

Only two things can manipulate the login list: the management agent, which is serialised (e.g. only one management request per target port can be ongoing at any point in time), and the session maintenance work item. So you're right, the lock-protected section is too small.

Quoted 11 lines

By the way, the use of spin_lock()/spin_unlock() is quite atypical. This API restricts you
- not to call a possibly sleeping function within the lock- protected section,
- not to take the lock in tasklet context or IRQ context.

So this locking API is quite rarely used: Anywhere where a mutex /could/ be used, but none of the locked sections ever need to sleep. This is a rather narrow use case.

Maybe you know all this but I thought I mention it anyway.

The session is only really inspected/maniuplated in the context of work items, so it's possible I could use mutexes - although in the target agent address handler I do check the node_id against the one in the session (and I should really check the generation too). Would memory barriers be enough for those or would locking be required?

Quoted 50 lines
Quoted 14 lines
drivers/target/sbp/sbp_login.c new file +14 -0
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}

drivers/target/sbp/sbp_login.c line 618

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {
+		pr_info("Waiting for reconnect from node: %016llx\n",
+			sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+}

[Note to self: When more awake, carefully review this peeking into fw_card internals, the generation accesses, and the card refcounting.]

Quoted 6 lines
drivers/target/sbp/sbp_login.c new file +6 -0
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+	struct sbp_login_descriptor *login, *temp;
+
+	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+

drivers/target/sbp/sbp_login.c line 635

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+	spin_lock(&sess->login_list_lock);
+	list_for_each_entry_safe(login, temp,&sess->login_list, link) {
+		spin_unlock(&sess->login_list_lock);
+		sbp_login_release(login, false);
+		spin_lock(&sess->login_list_lock);
+	}
+	spin_unlock(&sess->login_list_lock);
+
+	/* sbp_login_release() calls sbp_session_release() */
+}

This is wrong. Either something external protects the session_reconnect_expired() executing context from concurrent manipulations of sess->login_list. Then you don't need to take the lock here in the first place.

Quoted 7 lines

Or there is no such external serialization measure. Then you must not drop the list lock in the loop body.

In the latter case, an easy fix would be to move the expired logins to a local temporary list while holding the lock, then release each item from the temporary list without holding the lock.

Yes deep down I knew it was wrong, and it'll be the latter case of needing more locking than it has. I guess I need to protect access to the entire session really. Possibly even rwlocks due to only the management processes ever changing anything, but lots of reads during command handling.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
#65 lore raw

Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

Quoted 5 lines · On Feb 16 Chris Boot wrote:

The session is only really inspected/maniuplated in the context of work items, so it's possible I could use mutexes - although in the target agent address handler I do check the node_id against the one in the session (and I should really check the generation too). Would memory barriers be enough for those or would locking be required?

A node ID itself is an incomplete datum. Only a card--generation--node_ID tuple is complete.

If generation and node ID are written and read concurrently, you have a couple of choices to do this consistently: You could atomically write and read an integer which contains 8 bits of generation and 16 bits of node ID. Or you could serialize write and read accesses by a lock or mutex. Or you could force the write side to write node ID before generation (with an smp_wmb between to enforce order), and the read side to read generation before node ID (with an smp_rmb to enforce order).

The latter method leaves a brief window in which a reader could pick up a current node ID together with an outdated generation. This is harmless because the OHCI hardware and respective precautions in firewire-ohci prevents transaction subactions with outdated generation to go out to the wire.

It is generally recommended to implement such accesses in drivers with locks (spinlocks or mutexes). Among the various means to deal with concurrency issues, locks seem to be best understood by us simple folk who hack on drivers. :-)

[...]

Quoted 4 lines

I guess I need to protect access to
the entire session really. Possibly even rwlocks due to only the management processes ever changing anything, but lots of reads during command handling.

The use case for rwlocks is not really the case where infrequent write access meets frequent read access. Rather, the use case is when it is important to reduce or prevent contention between readers.

Rwlocks come with their own downsides though. I guess the somewhat costlier lock implementation could counter the benefit of allowing concurrent readers. Or maybe latency spikes around a write access could be an issue.

I believe I have read somewhere that one should rather use a simple spinlock unless exhaustive tests prove that an rwlock really performs better. Furthermore, in many if not all use cases of rwlocks, RCU is available as another alternative. RCU comes with its own set of downsides though, for example not being as well and widely understood by programmers compared to locking, being less easy to debug (may have improved recently), and posing some challenges to RT-PREEMPT kernels.

AFAIU the above considerations cannot be applied 100 % to able-to-sleep reader-writer locks, i.e. the kernel's rwsem. Still, the use case of an rwsem (in contrast to a mutex) is not particularly where a datum is rarely written and often read, but where it is desirable to let multiple readers not block each other.

[Somebody correct me where I'm wrong.]

PS, I cloned your git tree not long ago, but again various distractions kept me from having a broader look at your code...

Signature · 3 lines
Stefan Richter
-=====-===-- --== ---==
http://arcgraph.de/sr/
#66 lore raw

Re: [PATCH v2 08/11] firewire-sbp-target: Add sbp_login.{c,h}

Quoted 1 line · Stefan Richter

On Feb 16 Chris Boot wrote:

[ . . . ]

Quoted 14 lines
Quoted 4 lines

I guess I need to protect access to
the entire session really. Possibly even rwlocks due to only the management processes ever changing anything, but lots of reads during command handling.

The use case for rwlocks is not really the case where infrequent write access meets frequent read access. Rather, the use case is when it is important to reduce or prevent contention between readers.

Rwlocks come with their own downsides though. I guess the somewhat costlier lock implementation could counter the benefit of allowing concurrent readers. Or maybe latency spikes around a write access could be an issue.

Quoted 7 lines

I believe I have read somewhere that one should rather use a simple spinlock unless exhaustive tests prove that an rwlock really performs better. Furthermore, in many if not all use cases of rwlocks, RCU is available as another alternative. RCU comes with its own set of downsides though, for example not being as well and widely understood by programmers compared to locking, being less easy to debug (may have improved recently), and posing some challenges to RT-PREEMPT kernels.

The preemptible RCU implementations (TREE_PREEMPT_RCU and TINY_PREEMPT_RCU) handle the -rt tree.

Quoted 7 lines

AFAIU the above considerations cannot be applied 100 % to able-to-sleep reader-writer locks, i.e. the kernel's rwsem. Still, the use case of an rwsem (in contrast to a mutex) is not particularly where a datum is rarely written and often read, but where it is desirable to let multiple readers not block each other.

[Somebody correct me where I'm wrong.]

And you can use SRCU if readers need to block.

That said, even I agree that RCU is not always the right tool for the job.

							;-), Paul
Quoted 12 lines

PS, I cloned your git tree not long ago, but again various distractions kept me from having a broader look at your code...

Signature · 8 lines
Stefan Richter
-=====-===-- --== ---==
http://arcgraph.de/sr/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
author maintainer
#67 lore raw

PATCH v2 9/11 firewire-sbp-target: Add sbp_target_agent.{c,h}

In reply to Chris Boot

This implements the SBP-2 Command Block Agent, or Target Agent. This is what receives SCSI commands and forwards them to the target framework.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_target_agent.c |  356 +++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_target_agent.h |   27 +++
 2 files changed, 383 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_target_agent.c
 create mode 100644 drivers/target/sbp/sbp_target_agent.h
drivers/target/sbp/sbp_target_agent.c new file +356 -0
new file mode 100644
index 0000000..66b0b78
@@ -0,0 +1,356 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	__be32 state;
+
+	switch (tcode) {
+	case TCODE_READ_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		state = cpu_to_be32(atomic_read(&agent->state));
+		memcpy(data, &state, sizeof(state));
+
+		return RCODE_COMPLETE;
+	
+	case TCODE_WRITE_QUADLET_REQUEST:
+		/* ignored */
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_RESET\n");
+		atomic_set(&agent->state, AGENT_STATE_RESET);
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+	int ret;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();
+
+		agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+
+		pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
+			agent->orb_pointer);
+
+		ret = queue_work(sbp_workqueue, &agent->work);
+		if (!ret)
+			return RCODE_CONFLICT_ERROR;
+
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_BLOCK_REQUEST:
+		pr_debug("tgt_agent ORB_POINTER READ\n");
+		addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	int ret;
+
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();
+
+		pr_debug("tgt_agent DOORBELL\n");
+
+		ret = queue_work(sbp_workqueue, &agent->work);
+		if (!ret)
+			return RCODE_CONFLICT_ERROR;
+
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_QUADLET_REQUEST:
+		return RCODE_COMPLETE;
+	
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+		atomic_set(&agent->login->unsolicited_status_enable, 1);
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_QUADLET_REQUEST:
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static void tgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_target_agent *agent = callback_data;
+	int rcode = RCODE_ADDRESS_ERROR;
+
+	/* turn offset into the offset from the start of the block */
+	offset -= agent->handler.offset;
+
+	if (source != agent->login->sess->node_id) {
+		pr_notice("ignoring request from foreign node (%x != %x)\n",
+				source, agent->login->sess->node_id);
+		fw_send_response(card, request, RCODE_TYPE_ERROR);
+		return;
+	}
+
+	if (offset == 0x00 && length == 4) {
+		/* AGENT_STATE */
+		rcode = tgt_agent_rw_agent_state(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x04 && length == 4) {
+		/* AGENT_RESET */
+		rcode = tgt_agent_rw_agent_reset(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x08 && length == 8) {
+		/* ORB_POINTER */
+		rcode = tgt_agent_rw_orb_pointer(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x10 && length == 4) {
+		/* DOORBELL */
+		rcode = tgt_agent_rw_doorbell(card, tcode,
+			generation, data, agent);
+	} else if (offset == 0x14 && length == 4) {
+		/* UNSOLICITED_STATUS_ENABLE */
+		rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+			generation, data, agent);
+	}
+
+	fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+	struct sbp_target_request *req =
+		container_of(work, struct sbp_target_request, work);
+
+	switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+	case 0:/* Format specified by this standard */
+		sbp_handle_command(req);
+		return;
+	case 1: /* Reserved for future standardization */
+	case 2: /* Vendor-dependent */
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	case 3: /* Dummy ORB */
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_DUMMY_ORB_COMPLETE));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	default:
+		BUG();
+	}
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+	struct sbp_target_agent *agent =
+		container_of(work, struct sbp_target_agent, work);
+	struct sbp_session *sess = agent->login->sess;
+	struct sbp_target_request *req;
+	int ret;
+
+	smp_rmb();
+	if (atomic_read(&agent->state) != AGENT_STATE_ACTIVE)
+		return;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req) {
+		atomic_cmpxchg(&agent->state, AGENT_STATE_ACTIVE,
+				AGENT_STATE_DEAD);
+		return;
+	}
+
+	req->agent = agent;
+	req->orb_pointer = agent->orb_pointer;
+	req->status.status = cpu_to_be32(
+			STATUS_BLOCK_ORB_OFFSET_HIGH(req->orb_pointer >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_pointer & 0xfffffffc);
+	INIT_WORK(&req->work, tgt_agent_process_work);
+
+	/* read in the ORB */
+	ret = fw_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		req->orb_pointer, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("tgt_orb fetch failed: %x\n", ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(1) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		atomic_cmpxchg(&agent->state, AGENT_STATE_ACTIVE,
+				AGENT_STATE_DEAD);
+		return;
+	}
+
+	pr_debug("tgt_orb ptr:0x%llx next_orb:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+			req->orb_pointer,
+			sbp2_pointer_to_addr(&req->orb.next_orb),
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			be32_to_cpu(req->orb.misc));
+
+	if (req->orb_pointer >> 32)
+		pr_debug("ORB with high bits set\n");
+
+	if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+		/* NULL next-ORB */
+		req->status.status |= cpu_to_be32(
+				STATUS_BLOCK_SRC(STATUS_SRC_ORB_FINISHED));
+	} else {
+		/* non-NULL next-ORB */
+		req->status.status |= cpu_to_be32(
+				STATUS_BLOCK_SRC(STATUS_SRC_ORB_CONTINUING));
+	}
+
+	queue_work(sbp_workqueue, &req->work);
+
+	/* check if we should carry on processing */
+	if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+		/* null next_orb */
+		atomic_cmpxchg(&agent->state, AGENT_STATE_ACTIVE,
+				AGENT_STATE_SUSPENDED);
+	} else {
+		pr_debug("non-NULL next-ORB\n");
+		agent->orb_pointer = sbp2_pointer_to_addr(&req->orb.next_orb);
+		queue_work(sbp_workqueue, &agent->work);
+	}
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login)
+{
+	struct sbp_target_agent *agent;
+	int ret;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	agent->handler.length = 0x20;
+	agent->handler.address_callback = tgt_agent_rw;
+	agent->handler.callback_data = agent;
+
+	agent->login = login;
+	atomic_set(&agent->state, AGENT_STATE_RESET);
+	INIT_WORK(&agent->work, tgt_agent_fetch_work);
+	agent->orb_pointer = (u64)-1;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+		&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}
+
drivers/target/sbp/sbp_target_agent.h new file +27 -0
new file mode 100644
index 0000000..cddcc2f
@@ -0,0 +1,27 @@
+
+struct sbp_target_agent {
+	struct fw_address_handler handler;
+	struct sbp_login_descriptor *login;
+	atomic_t state;
+	struct work_struct work;
+	u64 orb_pointer;
+};
+
+struct sbp_target_request {
+	struct sbp_target_agent *agent;
+	u64 orb_pointer;
+	struct sbp_command_block_orb orb;
+	struct sbp_status_block status;
+	struct work_struct work;
+
+	struct se_cmd se_cmd;
+	struct sbp_page_table_entry *pg_tbl;
+	void *cmd_buf;
+	void *data_buf;
+
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
Signature · 1 line
1.7.9
#68 lore raw

Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

Quoted 2 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_target_agent.c new file +0 -0

[...]

Quoted 9 lines
drivers/target/sbp/sbp_target_agent.c new file +9 -0
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+	int ret;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:

drivers/target/sbp/sbp_target_agent.c line 93

Quoted 10 lines
drivers/target/sbp/sbp_target_agent.c new file +10 -0
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();

Why the double state change?

And as asked at the patch, which writes are the barriers meant to order, and how does the corresponding read side look like? Or are these barriers not actually needed after all?

[...]

drivers/target/sbp/sbp_target_agent.c line 355

Quoted 8 lines
drivers/target/sbp/sbp_target_agent.c new file +8 -0
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}

So, asking once more without having read the code in full yet: Are you sure that agent->state is not going to change anymore after you tested it here?

Signature · 3 lines
Stefan Richter
-=====-===-- --=- -====
http://arcgraph.de/sr/
author maintainer
#69 lore raw

Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

Quoted 17 lines · Stefan Richter
Quoted 2 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_target_agent.c new file +0 -0

[...]

Quoted 13 lines
drivers/target/sbp/sbp_target_agent.c new file +13 -0
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+	int ret;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
Quoted 8 lines

drivers/target/sbp/sbp_target_agent.c line 93

Quoted 6 lines
drivers/target/sbp/sbp_target_agent.c new file +6 -0
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();

Why the double state change?

Because the SBP spec differentiates between the RESET state, which happens after the agent initialises or is sent an explicit reset request, and when it's suspended between requests...

Quoted 3 lines

And as asked at the patch, which writes are the barriers meant to order, and how does the corresponding read side look like? Or are these barriers not actually needed after all?

...of course this is another time when my use of atomics and memory barriers is entirely wrong. I should most likely be using locking here.

Quoted 13 lines

[...]

drivers/target/sbp/sbp_target_agent.c line 355

Quoted 8 lines
drivers/target/sbp/sbp_target_agent.c new file +8 -0
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}

So, asking once more without having read the code in full yet: Are you sure that agent->state is not going to change anymore after you tested it here?

Nope. At least in this case I can unregister the address handler before I check if I need to flush the work item.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
#70 lore raw

Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

Quoted 27 lines · On Feb 16 Chris Boot wrote:
Quoted 17 lines · Stefan Richter
Quoted 2 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_target_agent.c new file +0 -0

[...]

Quoted 13 lines
drivers/target/sbp/sbp_target_agent.c new file +13 -0
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+	int ret;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
Quoted 8 lines

drivers/target/sbp/sbp_target_agent.c line 93

Quoted 6 lines
drivers/target/sbp/sbp_target_agent.c new file +6 -0
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();

Why the double state change?

Quoted 3 lines

Because the SBP spec differentiates between the RESET state, which happens after the agent initialises or is sent an explicit reset request, and when it's suspended between requests...

OK, right, there are the state transitions Reset-->Active and Suspended-->Active. Though you implement the former as a swift Reset-->Suspended-->Active. Which does indeed work, provided that there is no other concurrent context which could transition from Suspended to Anything-but-Active.

Quoted 21 lines
Quoted 3 lines

And as asked at the patch, which writes are the barriers meant to order, and how does the corresponding read side look like? Or are these barriers not actually needed after all?

...of course this is another time when my use of atomics and memory barriers is entirely wrong. I should most likely be using locking here.

Quoted 13 lines

[...]

drivers/target/sbp/sbp_target_agent.c line 355

Quoted 8 lines
drivers/target/sbp/sbp_target_agent.c new file +8 -0
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}

So, asking once more without having read the code in full yet: Are you sure that agent->state is not going to change anymore after you tested it here?

Quoted 2 lines

Nope. At least in this case I can unregister the address handler before I check if I need to flush the work item.

Yep, first unregister the handler, then wait for the work to finish, then free the data.

And as discussed off-list today, firewire-core should be improved to guarantee you that the handler isn't still running anywhere when fw_core_remove_address_handler() returns.

Signature · 3 lines
Stefan Richter
-=====-===-- --=- =--=-
http://arcgraph.de/sr/
author maintainer
#71 lore raw

Re: [PATCH v2 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

Quoted 32 lines · Stefan Richter
Quoted 27 lines · On Feb 16 Chris Boot wrote:
Quoted 17 lines · Stefan Richter
Quoted 2 lines · On Feb 15 Chris Boot wrote:
drivers/target/sbp/sbp_target_agent.c new file +0 -0

[...]

Quoted 13 lines
drivers/target/sbp/sbp_target_agent.c new file +13 -0
+static int tgt_agent_rw_orb_pointer(struct fw_card *card,
+	int tcode, int generation, void *data,
+	struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+	int ret;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:
+		smp_wmb();
+		atomic_cmpxchg(&agent->state,
+				AGENT_STATE_RESET, AGENT_STATE_SUSPENDED);
+		smp_wmb();
Quoted 8 lines

drivers/target/sbp/sbp_target_agent.c line 93

Quoted 6 lines
drivers/target/sbp/sbp_target_agent.c new file +6 -0
+		if (atomic_cmpxchg(&agent->state,
+					AGENT_STATE_SUSPENDED,
+					AGENT_STATE_ACTIVE)
+				!= AGENT_STATE_SUSPENDED)
+			return RCODE_CONFLICT_ERROR;
+		smp_wmb();

Why the double state change?

Quoted 3 lines

Because the SBP spec differentiates between the RESET state, which happens after the agent initialises or is sent an explicit reset request, and when it's suspended between requests...

Quoted 5 lines

OK, right, there are the state transitions Reset-->Active and Suspended-->Active. Though you implement the former as a swift Reset-->Suspended-->Active. Which does indeed work, provided that there is no other concurrent context which could transition from Suspended to Anything-but-Active.

I'll rewrite this bit to use a lock to protect the state struct member so it isn't so strange. That would also avoid the unusual double state change.

Quoted 24 lines
Quoted 21 lines
Quoted 3 lines

And as asked at the patch, which writes are the barriers meant to order, and how does the corresponding read side look like? Or are these barriers not actually needed after all?

...of course this is another time when my use of atomics and memory barriers is entirely wrong. I should most likely be using locking here.

Quoted 13 lines

[...]

drivers/target/sbp/sbp_target_agent.c line 355

Quoted 8 lines
drivers/target/sbp/sbp_target_agent.c new file +8 -0
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	if (atomic_read(&agent->state) == AGENT_STATE_ACTIVE)
+		flush_work_sync(&agent->work);
+
+	fw_core_remove_address_handler(&agent->handler);
+	kfree(agent);
+}

So, asking once more without having read the code in full yet: Are you sure that agent->state is not going to change anymore after you tested it here?

Quoted 2 lines

Nope. At least in this case I can unregister the address handler before I check if I need to flush the work item.

Quoted 6 lines

Yep, first unregister the handler, then wait for the work to finish, then free the data.

And as discussed off-list today, firewire-core should be improved to guarantee you that the handler isn't still running anywhere when fw_core_remove_address_handler() returns.

Great, thanks for clearing that up.

Cheers,
Chris

Signature · 2 lines
Chris Boot
[email protected]
author maintainer
#72 lore raw

PATCH v2 10/11 firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}

In reply to Chris Boot

Miscellaneous functions for dealing with SCSI commands, status, sense data and data read/write. This is where the real grunt work of pushing data in and out of the FireWire bus happens.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_scsi_cmnd.c |  360 ++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_scsi_cmnd.h |    6 +
 2 files changed, 366 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
 create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
drivers/target/sbp/sbp_scsi_cmnd.c new file +360 -0
new file mode 100644
index 0000000..e0b4b71
@@ -0,0 +1,360 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "sbp_base.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+/*
+ * Wraps fw_run_transaction taking into account page size and max payload, and
+ * retries the transaction if it fails
+ */
+static int sbp_run_transaction(struct sbp_target_request *req, int tcode,
+	unsigned long long offset, void *payload, size_t length)
+{
+	struct sbp_login_descriptor *login = req->agent->login;
+	struct sbp_session *sess = login->sess;
+	int ret, speed, max_payload, pg_size, seg_off = 0, seg_len;
+
+	speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+	max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+	pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+
+	if (pg_size) {
+		pr_err("sbp_run_transaction: page size ignored\n");
+		pg_size = 0x100 << pg_size;
+	}
+
+	while (seg_off < length) {
+		seg_len = length - seg_off;
+		if (seg_len > max_payload)
+			seg_len = max_payload;
+
+		/* FIXME: take page_size into account */
+
+		/* FIXME: retry failed data transfers */
+		ret = fw_run_transaction(sess->card, tcode,
+				sess->node_id, sess->generation, speed,
+				offset + seg_off, payload + seg_off, seg_len);
+		if (ret != RCODE_COMPLETE) {
+			pr_debug("sbp_run_transaction: txn failed: %x\n", ret);
+			return -EIO;
+		}
+
+		seg_off += seg_len;
+	}
+
+	return 0;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+	int ret, cmd_len, copy_len;
+
+	cmd_len = scsi_command_size(req->orb.command_block);
+
+	req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+	if (!req->cmd_buf)
+		return -ENOMEM;
+
+	memcpy(req->cmd_buf, req->orb.command_block,
+		min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+	if (cmd_len > sizeof(req->orb.command_block)) {
+		pr_debug("sbp_fetch_command: filling in long command\n");
+		copy_len = cmd_len - sizeof(req->orb.command_block);
+
+		ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+			req->orb_pointer + sizeof(req->orb),
+			req->cmd_buf + sizeof(req->orb.command_block),
+			copy_len);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+	int pg_tbl_sz, ret;
+	struct sbp_page_table_entry *pg_tbl;
+
+	if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+		return 0;
+
+	pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+		sizeof(struct sbp_page_table_entry);
+
+	pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+	if (!pg_tbl)
+		return -ENOMEM;
+
+	ret = sbp_run_transaction(req, TCODE_READ_BLOCK_REQUEST,
+		sbp2_pointer_to_addr(&req->orb.data_descriptor),
+		pg_tbl, pg_tbl_sz);
+	if (ret) {
+		kfree(pg_tbl);
+		return ret;
+	}
+
+	req->pg_tbl = pg_tbl;
+	return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req,
+	u32 *data_len, enum dma_data_direction *data_dir)
+{
+	int data_size, direction, idx;
+
+	data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+	direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+	if (!data_size) {
+		*data_len = 0;
+		*data_dir = DMA_NONE;
+		return;
+	}
+
+	*data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+	if (req->pg_tbl) {
+		*data_len = 0;
+		for (idx = 0; idx < data_size; idx++) {
+			*data_len += be16_to_cpu(
+					req->pg_tbl[idx].segment_length);
+		}
+	} else {
+		*data_len = data_size;
+	}
+}
+
+void sbp_handle_command(struct sbp_target_request *req)
+{
+	struct sbp_login_descriptor *login = req->agent->login;
+	struct sbp_session *sess = login->sess;
+	int ret, unpacked_lun;
+	u32 data_length;
+	enum dma_data_direction data_dir;
+
+	ret = sbp_fetch_command(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	ret = sbp_fetch_page_table(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+			ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	unpacked_lun = req->agent->login->lun->unpacked_lun;
+	sbp_calc_data_length_direction(req, &data_length, &data_dir);
+
+	pr_debug("sbp_handle_command unpacked_lun:%d data_len:%d data_dir:%d\n",
+			unpacked_lun, data_length, data_dir);
+
+	target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+			req->sense_buf, unpacked_lun, data_length,
+			MSG_SIMPLE_TAG, data_dir, 0);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+int sbp_rw_data(struct sbp_target_request *req)
+{
+	int ret, tcode;
+
+	tcode = (req->se_cmd.data_direction == DMA_TO_DEVICE) ?
+		TCODE_READ_BLOCK_REQUEST :
+		TCODE_WRITE_BLOCK_REQUEST;
+
+	if (req->pg_tbl) {
+		int idx, offset = 0, data_size;
+
+		data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+		for (idx = 0; idx < data_size; idx++) {
+			struct sbp_page_table_entry *pte = &req->pg_tbl[idx];
+			int pte_len = be16_to_cpu(pte->segment_length);
+			u64 pte_offset =
+				(u64)be16_to_cpu(pte->segment_base_hi) << 32 |
+				be32_to_cpu(pte->segment_base_lo);
+
+			ret = sbp_run_transaction(req, tcode, pte_offset,
+					req->data_buf + offset, pte_len);
+			if (ret)
+				break;
+
+			offset += pte_len;
+		}
+	} else {
+		ret = sbp_run_transaction(req, tcode,
+				sbp2_pointer_to_addr(&req->orb.data_descriptor),
+				req->data_buf, req->se_cmd.data_length);
+	}
+
+	return ret;
+}
+
+int sbp_send_status(struct sbp_target_request *req)
+{
+	int ret, length;
+	struct sbp_login_descriptor *login = req->agent->login;
+
+	length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+	ret = sbp_run_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+			login->status_fifo_addr, &req->status, length);
+	if (ret) {
+		pr_debug("sbp_send_status: write failed: %d\n", ret);
+		return ret;
+	}
+
+	pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
+			req->orb_pointer);
+
+	return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+	u8 *sense = req->sense_buf;
+	u8 *status = req->status.data;
+
+	WARN_ON(se_cmd->scsi_sense_length < 18);
+
+	switch (sense[0] & 0x7f) { 		/* sfmt */
+	case 0x70: /* current, fixed */
+		status[0] = 0 << 6;
+		break;
+	case 0x71: /* deferred, fixed */
+		status[0] = 1 << 6;
+		break;
+	case 0x72: /* current, descriptor */
+	case 0x73: /* deferred, descriptor */
+	default:
+		/*
+		 * TODO: SBP-3 specifies what we should do with descriptor
+		 * format sense data
+		 */
+		pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+			sense[0]);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+		return;
+	}
+
+	status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+	status[1] =
+		(sense[0] & 0x80) |		/* valid */
+		((sense[2] & 0xe0) >> 1) |	/* mark, eom, ili */
+		(sense[2] & 0x0f);		/* sense_key */
+	status[2] = se_cmd->scsi_asc;		/* sense_code */
+	status[3] = se_cmd->scsi_ascq;		/* sense_qualifier */
+
+	/* information */
+	status[4] = sense[3];
+	status[5] = sense[4];
+	status[6] = sense[5];
+	status[7] = sense[6];
+
+	/* CDB-dependent */
+	status[8] = sense[8];
+	status[9] = sense[9];
+	status[10] = sense[10];
+	status[11] = sense[11];
+
+	/* fru */
+	status[12] = sense[14];
+
+	/* sense_key-dependent */
+	status[13] = sense[15];
+	status[14] = sense[16];
+	status[15] = sense[17];
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_DEAD(0) |
+		STATUS_BLOCK_LEN(5) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+int sbp_send_sense(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+
+	if (se_cmd->scsi_sense_length) {
+		sbp_sense_mangle(req);
+	} else {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+	}
+
+	return sbp_send_status(req);
+}
+
+void sbp_free_request(struct sbp_target_request *req)
+{
+	kfree(req->pg_tbl);
+	kfree(req->cmd_buf);
+	kfree(req->data_buf);
+	kfree(req);
+}
drivers/target/sbp/sbp_scsi_cmnd.h new file +6 -0
new file mode 100644
index 0000000..5e82b25
@@ -0,0 +1,6 @@
+
+void sbp_handle_command(struct sbp_target_request *req);
+int sbp_rw_data(struct sbp_target_request *req);
+int sbp_send_status(struct sbp_target_request *req);
+int sbp_send_sense(struct sbp_target_request *req);
+void sbp_free_request(struct sbp_target_request *req);
Signature · 1 line
1.7.9
author maintainer
#73 lore raw

PATCH v2 11/11 firewire-sbp-target: Add to target Kconfig and Makefile

In reply to Chris Boot

This commit also adds an entry to the MAINTAINERS file.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 MAINTAINERS             |    9 +++++++++
 drivers/target/Kconfig  |    1 +
 drivers/target/Makefile |    1 +
 3 files changed, 11 insertions(+), 0 deletions(-)
MAINTAINERS +9 -0
index c0f348d..252aa72 100644
@@ -2695,6 +2695,15 @@ T:	git git://git.alsa-project.org/alsa-kernel.git
 S:	Maintained
 F:	sound/firewire/
 
+FIREWIRE SBP-2 TARGET
+M:  Chris Boot <[email protected]>
+L:  [email protected]
+L:  [email protected]
+L:  [email protected]
+T:  git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S:  Maintained
+F:  drivers/target/sbp/
+
 FIREWIRE SUBSYSTEM
 M:	Stefan Richter <[email protected]>
 L:	[email protected]
drivers/target/Kconfig +1 -0
index fc5fa9f..2cfa467 100644
@@ -41,5 +41,6 @@ source "drivers/target/tcm_fc/Kconfig"
 source "drivers/target/iscsi/Kconfig"
 source "drivers/target/tcm_vhost/Kconfig"
 source "drivers/target/usb-gadget/Kconfig"
+source "drivers/target/sbp/Kconfig"
 
 endif
drivers/target/Makefile +1 -0
index 6b5f526..1ae8862 100644
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC)		+= tcm_fc/
 obj-$(CONFIG_ISCSI_TARGET)	+= iscsi/
 obj-$(CONFIG_TCM_VHOST)		+= tcm_vhost/
 obj-$(CONFIG_TARGET_USB_GADGET)	+= usb-gadget/
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += sbp/
Signature · 1 line
1.7.9
author maintainer
#74 lore raw

v3 cover firewire-sbp-target: FireWire SBP-2 SCSI target

In reply to Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Sorry this latest revision has been a long time coming. I was trying to chase down a crashing bug (but haven't been able to replicate it), changed my mind about a large portion of the code twice, and life got in the way as well!

Changes in v3:
* Updates for target framework API changes
* Attempt to make headers self-contained
* Remove bad use of atomics and memory barriers
* Rework use of locking
* Use system workqueues
* Wrap fw_run_transaction() to retry failed transactions
* Coding style fixes
* Fix a few bugs
* Overhaul sbp_rw_data()
* Overhaul target fetch agent

Changes in v2:
* Fixed some copy & paste issues
* Updated Kconfig (wording, depends, 'default n')
* Removed some unnecessary EXPORT_SYMBOL()s
* Merged sbp_util.{c,h} into sbp_configfs.c and sbp_base.h
* Merged sbp_proto.{c,h} into sbp_fabric.{c,h}
* Cleaned up comments and several printks
* Fixed a few minor bugs
* Create & use our own workqueue instead of using fw_workqueue
* Dropped the unused TFO->new_cmd_map and sbp_new_cmd()
* Overhauled and simplified tgt_agent_fetch_work()
* Removed some redundant members of struct sbp_target_request
* Removed struct sbp_lun and code to maintain redundant LUN list
* Added spinlock to struct sbp_session and use locking throughout
* Moved fw_card_{get,put,release}() into linux/firewire.h

author maintainer
#75 lore raw

PATCH v2 1/11 firewire: Add function to get speed from opaque struct fw_request

Sometimes it's useful to know the FireWire speed of the request that has just come in to a fw_address_handler callback. As struct fw_request is opaque we can't peek inside to get the speed out of the struct fw_packet that's just inside. For example, the SBP-2 spec says:

"The speed at which the block write request to the MANAGEMENT_AGENT register is received shall determine the speed used by the target for all subsequent requests to read the initiator’s configuration ROM, fetch ORB’s from initiator memory or store status at the initiator’s status_FIFO. Command block ORB’s separately specify the speed for requests addressed to the data buffer or page table."

[ ANSI T10/1155D Revision 4 page 53/54 ]

---
 drivers/firewire/core-transaction.c |    9 +++++++++
 include/linux/firewire.h            |    1 +
 2 files changed, 10 insertions(+)
drivers/firewire/core-transaction.c +9 -0
index dea2dcc..db8a965 100644
@@ -820,6 +820,15 @@ void fw_send_response(struct fw_card *card,
 }
 EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - returns speed at which the @request was received
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
 static void handle_exclusive_region_request(struct fw_card *card,
 					    struct fw_packet *p,
 					    struct fw_request *request,
include/linux/firewire.h +1 -0
index cdc9b71..4683130 100644
@@ -325,6 +325,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
 void fw_core_remove_address_handler(struct fw_address_handler *handler);
 void fw_send_response(struct fw_card *card,
 		      struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
 void fw_send_request(struct fw_card *card, struct fw_transaction *t,
 		     int tcode, int destination_id, int generation, int speed,
 		     unsigned long long offset, void *payload, size_t length,
Signature · 1 line
1.7.9.5
author maintainer
#76 lore raw

PATCH v2 2/11 firewire: Move fw_card kref functions into linux/firewire.h

In reply to Chris Boot

When writing a firewire driver that doesn't deal with struct fw_device objects (e.g. it only publishes FireWire units and doesn't subscribe to them), you likely need to keep referenced to struct fw_card objects so that you can send messages to other nodes. This patch moves fw_card_put(), fw_card_get() and fw_card_release() into the public include/linux/firewire.h header instead of drivers/firewire/core.h, and adds EXPORT_SYMBOL_GPL(fw_card_release).

The firewire-sbp-target module requires these so it can keep a reference to the fw_card object in order that it can fetch ORBs to execute and read/write related data and status information.

---
 drivers/firewire/core-card.c |    1 +
 drivers/firewire/core.h      |   15 ---------------
 include/linux/firewire.h     |   14 ++++++++++++++
 3 files changed, 15 insertions(+), 15 deletions(-)
drivers/firewire/core-card.c +1 -0
index cc595eb..f5552b3 100644
@@ -676,6 +676,7 @@ void fw_card_release(struct kref *kref)
 
 	complete(&card->done);
 }
+EXPORT_SYMBOL_GPL(fw_card_release);
 
 void fw_core_remove_card(struct fw_card *card)
 {
drivers/firewire/core.h +0 -15
index 9047f55..b5a2f61 100644
@@ -120,21 +120,6 @@ int fw_compute_block_crc(__be32 *block);
 void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
 void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
 
-static inline struct fw_card *fw_card_get(struct fw_card *card)
-{
-	kref_get(&card->kref);
-
-	return card;
-}
-
-void fw_card_release(struct kref *kref);
-
-static inline void fw_card_put(struct fw_card *card)
-{
-	kref_put(&card->kref, fw_card_release);
-}
-
-
 /* -cdev */
 
 extern const struct file_operations fw_device_ops;
include/linux/firewire.h +14 -0
index 4683130..e83c24a 100644
@@ -135,6 +135,20 @@ struct fw_card {
 	__be32 maint_utility_register;
 };
 
+static inline struct fw_card *fw_card_get(struct fw_card *card)
+{
+	kref_get(&card->kref);
+
+	return card;
+}
+
+void fw_card_release(struct kref *kref);
+
+static inline void fw_card_put(struct fw_card *card)
+{
+	kref_put(&card->kref, fw_card_release);
+}
+
 struct fw_attribute_group {
 	struct attribute_group *groups[2];
 	struct attribute_group group;
Signature · 1 line
1.7.9.5
author maintainer
#77 lore raw

PATCH v2 3/11 firewire-sbp-target: Add Kconfig, Makefile and TODO

In reply to Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/Kconfig  |   13 +++++++++++++
 drivers/target/sbp/Makefile |   11 +++++++++++
 drivers/target/sbp/TODO     |    7 +++++++
 3 files changed, 31 insertions(+)
 create mode 100644 drivers/target/sbp/Kconfig
 create mode 100644 drivers/target/sbp/Makefile
 create mode 100644 drivers/target/sbp/TODO
drivers/target/sbp/Kconfig new file +13 -0
new file mode 100644
index 0000000..c9f85e9
@@ -0,0 +1,13 @@
+
+config FIREWIRE_SBP_TARGET
+	tristate "FireWire SBP-2 fabric module"
+	depends on FIREWIRE && EXPERIMENTAL
+	help
+	  Say Y or M here to enable SCSI target functionality over FireWire.
+	  This enables you to expose SCSI devices to other nodes on the FireWire
+	  bus, for example hard disks. Similar to FireWire Target Disk mode on
+	  many Apple computers.
+
+	  To compile this driver as a module, say M here: The module will be
+	  called firewire-sbp-target.
+
drivers/target/sbp/Makefile new file +11 -0
new file mode 100644
index 0000000..c8f73f4
@@ -0,0 +1,11 @@
+
+firewire-sbp-target-y += \
+	sbp_configfs.o \
+	sbp_fabric.o \
+	sbp_login.o \
+	sbp_management_agent.o \
+	sbp_scsi_cmnd.o \
+	sbp_target_agent.o
+
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += firewire-sbp-target.o
+
drivers/target/sbp/TODO new file +7 -0
new file mode 100644
index 0000000..eaec1c9
@@ -0,0 +1,7 @@
+* Force-terminate sessions when disabling targets
+* Ability to have several SCSI commands in-flight (TCQ?)
+* Retry failed FireWire transactions a few times with exponential backoff
+* Take into account the page_size field for transfers and/or page tables
+* Handle descriptor format sense data
+* Implement QUERY LOGINS management ORB
+* Implement TASK MANAGEMENT functionality
Signature · 1 line
1.7.9.5
author maintainer
#78 lore raw

PATCH v2 4/11 firewire-sbp-target: Add sbp_base.h header

In reply to Chris Boot

This header contains defines and structures that are common to many of the modules of the target code. This includes SBP-2 protocol structures and constants as well as a few structs for setting up the target, LUN login information and session setup.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_base.h |  211 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 211 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_base.h
drivers/target/sbp/sbp_base.h new file +211 -0
new file mode 100644
index 0000000..fa6a39b
@@ -0,0 +1,211 @@
+#ifndef _SBP_BASE_H
+#define _SBP_BASE_H
+
+#include <linux/firewire.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <target/target_core_base.h>
+
+#define SBP_VERSION  "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE	8
+
+#define MANAGEMENT_AGENT_STATE_IDLE	0
+#define MANAGEMENT_AGENT_STATE_BUSY	1
+
+#define ORB_NOTIFY(v)			(((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v)		(((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v)	(((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN			0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS		0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT		0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD		0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT			0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK		0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET		0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET	0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET		0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v)		(((v) >> 28) &   0x01)
+#define LOGIN_ORB_RESERVED(v)		(((v) >> 24) &   0x0f)
+#define LOGIN_ORB_RECONNECT(v)		(((v) >> 20) &   0x0f)
+#define LOGIN_ORB_LUN(v)		(((v) >>  0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v)	(((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v)	(((v) >>  0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v)	(((v) >>  0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v)		(((v) >>  0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v)		(((v) >> 27) &   0x01)
+#define CMDBLK_ORB_SPEED(v)		(((v) >> 24) &   0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v)	(((v) >> 20) &   0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v)	(((v) >> 19) &   0x01)
+#define CMDBLK_ORB_PG_SIZE(v)		(((v) >> 16) &   0x07)
+#define CMDBLK_ORB_DATA_SIZE(v)		(((v) >>  0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v)		(((v) &   0x03) << 30)
+#define STATUS_BLOCK_RESP(v)		(((v) &   0x03) << 28)
+#define STATUS_BLOCK_DEAD(v)		(((v) ? 1 : 0)  << 27)
+#define STATUS_BLOCK_LEN(v)		(((v) &   0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v)	(((v) &   0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v)	(((v) & 0xffff) <<  0)
+
+#define STATUS_SRC_ORB_CONTINUING	0
+#define STATUS_SRC_ORB_FINISHED		1
+#define STATUS_SRC_UNSOLICITED		2
+
+#define STATUS_RESP_REQUEST_COMPLETE	0
+#define STATUS_RESP_TRANSPORT_FAILURE	1
+#define STATUS_RESP_ILLEGAL_REQUEST	2
+#define STATUS_RESP_VENDOR_DEPENDENT	3
+
+#define SBP_STATUS_OK			0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP	1
+#define SBP_STATUS_SPEED_NOTSUPP	2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP	3
+#define SBP_STATUS_ACCESS_DENIED	4
+#define SBP_STATUS_LUN_NOTSUPP		5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL	6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL	8
+#define SBP_STATUS_FUNCTION_REJECTED	9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN	10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE	11
+#define SBP_STATUS_REQUEST_ABORTED	12
+#define SBP_STATUS_UNSPECIFIED_ERROR	0xff
+
+#define AGENT_STATE_RESET	0
+#define AGENT_STATE_ACTIVE	1
+#define AGENT_STATE_SUSPENDED	2
+#define AGENT_STATE_DEAD	3
+
+struct sbp2_pointer {
+	__be32 high;
+	__be32 low;
+};
+
+struct sbp_command_block_orb {
+	struct sbp2_pointer next_orb;
+	struct sbp2_pointer data_descriptor;
+	__be32 misc;
+	u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+	__be16 segment_length;
+	__be16 segment_base_hi;
+	__be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+	struct sbp2_pointer ptr1;
+	struct sbp2_pointer ptr2;
+	__be32 misc;
+	__be32 length;
+	struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+	__be32 status;
+	__be32 orb_low;
+	u8 data[24];
+};
+
+struct sbp_login_response_block {
+	__be32 misc;
+	struct sbp2_pointer command_block_agent;
+	__be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+	struct sbp_session *sess;
+	struct list_head link;
+
+	struct se_lun *lun;
+
+	u64 status_fifo_addr;
+	int exclusive;
+	u16 login_id;
+
+	struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+	spinlock_t lock;
+	struct se_session *se_sess;
+	struct list_head login_list;
+	struct delayed_work maint_work;
+
+	u64 guid; /* login_owner_EUI_64 */
+	int node_id; /* login_owner_ID */
+
+	struct fw_card *card;
+	int generation;
+	int speed;
+
+	int reconnect_hold;
+	u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+	/* Initiator EUI-64 */
+	u64 guid;
+	/* ASCII formatted GUID for SBP Initiator port */
+	char iport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_nodeacl() */
+	struct se_node_acl se_node_acl;
+};
+
+struct sbp_tpg {
+	/* Target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer back to sbp_tport */
+	struct sbp_tport *tport;
+	/* Returned by sbp_make_tpg() */
+	struct se_portal_group se_tpg;
+};
+
+struct sbp_tport {
+	/* Target Unit Identifier (EUI-64) */
+	u64 guid;
+	/* Target port name */
+	char tport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_tport() */
+	struct se_wwn tport_wwn;
+
+	struct sbp_tpg *tpg;
+
+	/* FireWire unit directory */
+	struct fw_descriptor unit_directory;
+
+	/* SBP Management Agent */
+	struct sbp_management_agent *mgt_agt;
+
+	/* Parameters */
+	int enable;
+	s32 directory_id;
+	int mgt_orb_timeout;
+	int max_reconnect_timeout;
+	int max_logins_per_lun;
+};
+
+extern struct target_fabric_configfs *sbp_fabric_configfs;
+extern const struct fw_address_region sbp_register_region;
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+	return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+		(be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+	ptr->high = cpu_to_be32(addr >> 32);
+	ptr->low = cpu_to_be32(addr);
+}
+
+#endif
Signature · 1 line
1.7.9.5
author maintainer
#79 lore raw

PATCH v2 5/11 firewire-sbp-target: Add sbp_configfs.c

In reply to Chris Boot

This is used to glue the target framework's configfs code to the target code, and what is used to create targets and link them to LUNs to export. The code to create the FireWire unit directory to advertise targets on the FireWire bus is also in here.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_configfs.c |  737 +++++++++++++++++++++++++++++++++++++
 1 file changed, 737 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_configfs.c
drivers/target/sbp/sbp_configfs.c new file +737 -0
new file mode 100644
index 0000000..5399616
@@ -0,0 +1,737 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_management_agent.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *sbp_fabric_configfs;
+
+/* FireWire address region for management and command block address handlers */
+const struct fw_address_region sbp_register_region = {
+	.start = CSR_REGISTER_BASE + 0x10000,
+	.end   = 0x1000000000000ULL,
+};
+
+static const u32 sbp_unit_directory_template[] = {
+	0x1200609e, /* unit_specifier_id: NCITS/T10 */
+	0x13010483, /* unit_sw_version: 1155D Rev 4 */
+	0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+	0x390104d8, /* command_set: SPC-2 */
+	0x3b000000, /* command_set_revision: 0 */
+	0x3c000001, /* firmware_revision: 1 */
+};
+
+static int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
+{
+	int i, count = 0;
+
+	spin_lock(&tpg->tpg_lun_lock);
+	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+		struct se_lun *se_lun = tpg->tpg_lun_list[i];
+
+		if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+			continue;
+
+		count++;
+	}
+	spin_unlock(&tpg->tpg_lun_lock);
+
+	return count;
+}
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+	int num_luns, num_entries, idx = 0, mgt_agt_addr, ret, i;
+	u32 *data;
+
+	if (tport->unit_directory.data) {
+		fw_core_remove_descriptor(&tport->unit_directory);
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	if (!tport->enable || !tport->tpg)
+		return 0;
+
+	num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
+
+	/*
+	 * Number of entries in the final unit directory:
+	 *  - all of those in the template
+	 *  - management_agent
+	 *  - unit_characteristics
+	 *  - reconnect_timeout
+	 *  - unit unique ID
+	 *  - one for each LUN
+	 *
+	 *  MUST NOT include leaf or sub-directory entries
+	 */
+	num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+	if (tport->directory_id != -1)
+		num_entries++;
+
+	/* allocate num_entries + 4 for the header and unique ID leaf */
+	data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* directory_length */
+	data[idx++] = num_entries << 16;
+
+	/* directory_id */
+	if (tport->directory_id != -1)
+		data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+	/* unit directory template */
+	memcpy(&data[idx], sbp_unit_directory_template,
+			sizeof(sbp_unit_directory_template));
+	idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+	/* management_agent */
+	mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+	data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+	/* unit_characteristics */
+	data[idx++] = 0x3a000000 |
+		(((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+		SBP_ORB_FETCH_SIZE;
+
+	/* reconnect_timeout */
+	data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+	/* unit unique ID (leaf is just after LUNs) */
+	data[idx++] = 0x8d000000 | (num_luns + 1);
+
+	spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+		struct se_lun *se_lun = tport->tpg->se_tpg.tpg_lun_list[i];
+		struct se_device *dev;
+		int type;
+
+		if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+			continue;
+
+		spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+		dev = se_lun->lun_se_dev;
+		type = dev->transport->get_device_type(dev);
+
+		/* logical_unit_number */
+		data[idx++] = 0x14000000 |
+			((type << 16) & 0x1f0000) |
+			(se_lun->unpacked_lun & 0xffff);
+
+		spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+	}
+	spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+	/* unit unique ID leaf */
+	data[idx++] = 2 << 16;
+	data[idx++] = tport->guid >> 32;
+	data[idx++] = tport->guid;
+
+	tport->unit_directory.length = idx;
+	tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+	tport->unit_directory.data = data;
+
+	ret = fw_core_add_descriptor(&tport->unit_directory);
+	if (ret < 0) {
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+	const char *cp;
+	char c, nibble;
+	int pos = 0, err;
+
+	*wwn = 0;
+	for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+		c = *cp;
+		if (c == '\n' && cp[1] == '\0')
+			continue;
+		if (c == '\0') {
+			err = 2;
+			if (pos != 16)
+				goto fail;
+			return cp - name;
+		}
+		err = 3;
+		if (isdigit(c))
+			nibble = c - '0';
+		else if (isxdigit(c) && (islower(c) || !strict))
+			nibble = tolower(c) - 'a' + 10;
+		else
+			goto fail;
+		*wwn = (*wwn << 4) | nibble;
+		pos++;
+	}
+	err = 4;
+fail:
+	printk(KERN_INFO "err %u len %zu pos %u\n",
+			err, cp - name, pos);
+	return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+	return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+		struct se_portal_group *se_tpg,
+		struct config_group *group,
+		const char *name)
+{
+	struct se_node_acl *se_nacl, *se_nacl_new;
+	struct sbp_nacl *nacl;
+	u64 guid = 0;
+	u32 nexus_depth = 1;
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+	if (!se_nacl_new)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+	 * when converting a NodeACL from demo mode -> explict
+	 */
+	se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+			name, nexus_depth);
+	if (IS_ERR(se_nacl)) {
+		sbp_release_fabric_acl(se_tpg, se_nacl_new);
+		return se_nacl;
+	}
+
+	nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	nacl->guid = guid;
+	sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+	return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+	core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+	kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+
+	return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	int ret;
+
+	if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
+		tport->enable = 0;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+		struct se_wwn *wwn,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	struct sbp_tpg *tpg;
+	unsigned long tpgt;
+	int ret;
+
+	if (strstr(name, "tpgt_") != name)
+		return ERR_PTR(-EINVAL);
+	if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+		return ERR_PTR(-EINVAL);
+
+	if (tport->tpg) {
+		pr_err("Only one TPG per Unit is possible.\n");
+		return ERR_PTR(-EBUSY);
+	}
+
+	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+	if (!tpg) {
+		pr_err("Unable to allocate struct sbp_tpg\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tpg->tport = tport;
+	tpg->tport_tpgt = tpgt;
+	tport->tpg = tpg;
+
+	/* default attribute values */
+	tport->enable = 0;
+	tport->directory_id = -1;
+	tport->mgt_orb_timeout = 15;
+	tport->max_reconnect_timeout = 5;
+	tport->max_logins_per_lun = 1;
+
+	tport->mgt_agt = sbp_management_agent_register(tport);
+	if (IS_ERR(tport->mgt_agt)) {
+		ret = PTR_ERR(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+			&tpg->se_tpg, (void *)tpg,
+			TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret < 0) {
+		sbp_management_agent_unregister(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	core_tpg_deregister(se_tpg);
+	sbp_management_agent_unregister(tport->mgt_agt);
+	tport->tpg = NULL;
+	kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+		struct target_fabric_configfs *tf,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport;
+	u64 guid = 0;
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+	if (!tport) {
+		pr_err("Unable to allocate struct sbp_tport\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tport->guid = guid;
+	sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+	return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+		struct target_fabric_configfs *tf,
+		char *page)
+{
+	return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+	&sbp_wwn_version.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_show_directory_id(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	if (tport->directory_id == -1)
+		return sprintf(page, "implicit\n");
+	else
+		return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (tport->enable) {
+		pr_err("Cannot change the directory_id on an active target.\n");
+		return -EBUSY;
+	}
+
+	if (strstr(page, "implicit") == page) {
+		tport->directory_id = -1;
+	} else {
+		if (kstrtoul(page, 16, &val) < 0)
+			return -EINVAL;
+		if (val > 0xffffff)
+			return -EINVAL;
+
+		tport->directory_id = val;
+	}
+
+	return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val != 0) && (val != 1))
+		return -EINVAL;
+
+	if (tport->enable == val)
+		return count;
+
+	if (val) {
+		if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
+			pr_err("Cannot enable a target with no LUNs!\n");
+			return -EINVAL;
+		}
+	} else {
+		/* XXX: force-shutdown sessions instead? */
+		spin_lock_bh(&se_tpg->session_lock);
+		if (!list_empty(&se_tpg->tpg_sess_list)) {
+			spin_unlock_bh(&se_tpg->session_lock);
+			return -EBUSY;
+		}
+		spin_unlock_bh(&se_tpg->session_lock);
+	}
+
+	tport->enable = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0) {
+		pr_err("Could not update Config ROM\n");
+		return ret;
+	}
+
+	return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+	&sbp_tpg_directory_id.attr,
+	&sbp_tpg_enable.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	if (tport->mgt_orb_timeout == val)
+		return count;
+
+	tport->mgt_orb_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 32767))
+		return -EINVAL;
+
+	if (tport->max_reconnect_timeout == val)
+		return count;
+
+	tport->max_reconnect_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	/* XXX: also check against current count? */
+
+	tport->max_logins_per_lun = val;
+
+	return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+	&sbp_tpg_attrib_mgt_orb_timeout.attr,
+	&sbp_tpg_attrib_max_reconnect_timeout.attr,
+	&sbp_tpg_attrib_max_logins_per_lun.attr,
+	NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+	.get_fabric_name		= sbp_get_fabric_name,
+	.get_fabric_proto_ident		= sbp_get_fabric_proto_ident,
+	.tpg_get_wwn			= sbp_get_fabric_wwn,
+	.tpg_get_tag			= sbp_get_tag,
+	.tpg_get_default_depth		= sbp_get_default_depth,
+	.tpg_get_pr_transport_id	= sbp_get_pr_transport_id,
+	.tpg_get_pr_transport_id_len	= sbp_get_pr_transport_id_len,
+	.tpg_parse_pr_out_transport_id	= sbp_parse_pr_out_transport_id,
+	.tpg_check_demo_mode		= sbp_check_true,
+	.tpg_check_demo_mode_cache	= sbp_check_true,
+	.tpg_check_demo_mode_write_protect = sbp_check_false,
+	.tpg_check_prod_mode_write_protect = sbp_check_false,
+	.tpg_alloc_fabric_acl		= sbp_alloc_fabric_acl,
+	.tpg_release_fabric_acl		= sbp_release_fabric_acl,
+	.tpg_get_inst_index		= sbp_tpg_get_inst_index,
+	.release_cmd			= sbp_release_cmd,
+	.shutdown_session		= sbp_shutdown_session,
+	.close_session			= sbp_close_session,
+	.sess_get_index			= sbp_sess_get_index,
+	.write_pending			= sbp_write_pending,
+	.write_pending_status		= sbp_write_pending_status,
+	.set_default_node_attributes	= sbp_set_default_node_attrs,
+	.get_task_tag			= sbp_get_task_tag,
+	.get_cmd_state			= sbp_get_cmd_state,
+	.queue_data_in			= sbp_queue_data_in,
+	.queue_status			= sbp_queue_status,
+	.queue_tm_rsp			= sbp_queue_tm_rsp,
+	.get_fabric_sense_len		= sbp_get_fabric_sense_len,
+	.set_fabric_sense_len		= sbp_set_fabric_sense_len,
+	.check_stop_free		= sbp_check_stop_free,
+
+	.fabric_make_wwn		= sbp_make_tport,
+	.fabric_drop_wwn		= sbp_drop_tport,
+	.fabric_make_tpg		= sbp_make_tpg,
+	.fabric_drop_tpg		= sbp_drop_tpg,
+	.fabric_post_link		= sbp_post_link_lun,
+	.fabric_pre_unlink		= sbp_pre_unlink_lun,
+	.fabric_make_np			= NULL,
+	.fabric_drop_np			= NULL,
+	.fabric_make_nodeacl		= sbp_make_nodeacl,
+	.fabric_drop_nodeacl		= sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+	if (!fabric) {
+		pr_err("target_fabric_configfs_init() failed\n");
+		return -ENOMEM;
+	}
+
+	fabric->tf_ops = sbp_ops;
+
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 */
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		pr_err("target_fabric_configfs_register() failed for SBP\n");
+		return ret;
+	}
+
+	sbp_fabric_configfs = fabric;
+
+	return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+	if (!sbp_fabric_configfs)
+		return;
+
+	target_fabric_configfs_deregister(sbp_fabric_configfs);
+	sbp_fabric_configfs = NULL;
+};
+
+static int __init sbp_init(void)
+{
+	int ret;
+
+	ret = sbp_register_configfs();
+	if (ret < 0)
+		return ret;
+
+	return 0;
+};
+
+static void sbp_exit(void)
+{
+	sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
Signature · 1 line
1.7.9.5
author maintainer
#80 lore raw

PATCH v2 6/11 firewire-sbp-target: Add sbp_fabric.{c,h}

In reply to Chris Boot

This serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_fabric.c |  314 +++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_fabric.h |   40 +++++
 2 files changed, 354 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_fabric.c
 create mode 100644 drivers/target/sbp/sbp_fabric.h
drivers/target/sbp/sbp_fabric.c new file +314 -0
new file mode 100644
index 0000000..5230851
@@ -0,0 +1,314 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_fabric.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+int sbp_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+int sbp_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+char *sbp_get_fabric_name(void)
+{
+	return "sbp";
+}
+
+char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	return &tport->tport_name[0];
+}
+
+u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	return tpg->tport_tpgt;
+}
+
+u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+	struct sbp_nacl *nacl;
+
+	nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+	if (!nacl) {
+		pr_err("Unable to alocate struct sbp_nacl\n");
+		return NULL;
+	}
+
+	return &nacl->se_node_acl;
+}
+
+void sbp_release_fabric_acl(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	kfree(nacl);
+}
+
+u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	sbp_free_request(req);
+}
+
+int sbp_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+void sbp_close_session(struct se_session *se_sess)
+{
+	return;
+}
+
+u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+int sbp_write_pending(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(
+				STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(
+				SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	transport_generic_process_write(se_cmd);
+
+	return 0;
+}
+
+int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+	return;
+}
+
+u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	/* only used for printk until we do TMRs */
+	return (u32)req->orb_pointer;
+}
+
+int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+int sbp_queue_status(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	return sbp_send_sense(req);
+}
+
+int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+	return 0;
+}
+
+u16 sbp_get_fabric_sense_len(void)
+{
+	return 0;
+}
+
+int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	transport_generic_free_cmd(&req->se_cmd, 0);
+	return 1;
+}
+
+/*
+ * Handlers for Serial Bus Protocol 2/3 (SBP-2 / SBP-3)
+ */
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+	/*
+	 * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+	 * This is defined in section 7.5.1 Table 362 in spc4r17
+	 */
+	return SCSI_PROTOCOL_SBP;
+}
+
+u32 sbp_get_pr_transport_id(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code,
+	unsigned char *buf)
+{
+	int ret;
+
+	/*
+	 * Set PROTOCOL IDENTIFIER to 3h for SBP
+	 */
+	buf[0] = SCSI_PROTOCOL_SBP;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 */
+	ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+	if (ret < 0)
+		pr_debug("sbp transport_id: invalid hex string\n");
+
+	/*
+	 * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+
+u32 sbp_get_pr_transport_id_len(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code)
+{
+	*format_code = 0;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 *
+	 * The SBP Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+char *sbp_parse_pr_out_transport_id(
+	struct se_portal_group *se_tpg,
+	const char *buf,
+	u32 *out_tid_len,
+	char **port_nexus_ptr)
+{
+	/*
+	 * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+	 * for initiator ports using SCSI over SBP Serial SCSI Protocol
+	 *
+	 * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+	 * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+	 * so we return the **port_nexus_ptr set to NULL.
+	 */
+	*port_nexus_ptr = NULL;
+	*out_tid_len = 24;
+
+	return (char *)&buf[8];
+}
drivers/target/sbp/sbp_fabric.h new file +40 -0
new file mode 100644
index 0000000..cf66eca
@@ -0,0 +1,40 @@
+
+#include <linux/types.h>
+#include <target/target_core_base.h>
+
+int sbp_check_true(struct se_portal_group *);
+int sbp_check_false(struct se_portal_group *);
+char *sbp_get_fabric_name(void);
+char *sbp_get_fabric_wwn(struct se_portal_group *);
+u16 sbp_get_tag(struct se_portal_group *);
+u32 sbp_get_default_depth(struct se_portal_group *);
+struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *);
+void sbp_release_fabric_acl(struct se_portal_group *,
+		struct se_node_acl *);
+u32 sbp_tpg_get_inst_index(struct se_portal_group *);
+void sbp_release_cmd(struct se_cmd *se_cmd);
+int sbp_shutdown_session(struct se_session *);
+void sbp_close_session(struct se_session *);
+u32 sbp_sess_get_index(struct se_session *);
+int sbp_write_pending(struct se_cmd *);
+int sbp_write_pending_status(struct se_cmd *);
+void sbp_set_default_node_attrs(struct se_node_acl *);
+u32 sbp_get_task_tag(struct se_cmd *);
+int sbp_get_cmd_state(struct se_cmd *);
+int sbp_queue_data_in(struct se_cmd *);
+int sbp_queue_status(struct se_cmd *);
+int sbp_queue_tm_rsp(struct se_cmd *);
+u16 sbp_set_fabric_sense_len(struct se_cmd *, u32);
+u16 sbp_get_fabric_sense_len(void);
+int sbp_check_stop_free(struct se_cmd *se_cmd);
+
+u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg);
+u32 sbp_get_pr_transport_id(struct se_portal_group *se_tpg,
+		struct se_node_acl *se_nacl, struct t10_pr_registration *pr_reg,
+		int *format_code, unsigned char *buf);
+u32 sbp_get_pr_transport_id_len(
+		struct se_portal_group *se_tpg, struct se_node_acl *se_nacl,
+		struct t10_pr_registration *pr_reg, int *format_code);
+char *sbp_parse_pr_out_transport_id(
+		struct se_portal_group *se_tpg, const char *buf,
+		u32 *out_tid_len, char **port_nexus_ptr);
Signature · 1 line
1.7.9.5
author maintainer
#81 lore raw

PATCH v2 7/11 firewire-sbp-target: Add sbp_management_agent.{c,h}

In reply to Chris Boot

This code implements the SBP-2 Management Agent. This is the first of two firewire address handlers that are used to communicate with the target. The Management Agent is used to handle login, reconnect and logout to a SCSI LUN as well as task management functions.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_management_agent.c |  255 +++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_management_agent.h |   34 ++++
 2 files changed, 289 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_management_agent.c
 create mode 100644 drivers/target/sbp/sbp_management_agent.h
drivers/target/sbp/sbp_management_agent.c new file +255 -0
new file mode 100644
index 0000000..71f7b2f
@@ -0,0 +1,255 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kref.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_scsi_cmnd.h"
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+	struct sbp_management_agent *agent =
+		container_of(work, struct sbp_management_agent, work);
+	struct sbp_management_request *req = agent->request;
+	int ret;
+	int status_data_len = 0;
+
+	/* fetch the ORB from the initiator */
+	ret = sbp_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		agent->orb_offset, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb fetch failed: %x\n", ret);
+		goto out;
+	}
+
+	pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
+		sbp2_pointer_to_addr(&req->orb.ptr1),
+		sbp2_pointer_to_addr(&req->orb.ptr2),
+		be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+		sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+	if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+		ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+		pr_err("mgt_orb bad request\n");
+		goto out;
+	}
+
+	switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+	case MANAGEMENT_ORB_FUNCTION_LOGIN:
+		sbp_management_request_login(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+		sbp_management_request_query_logins(agent, req,
+				&status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+		sbp_management_request_reconnect(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+		pr_notice("SET PASSWORD not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+		sbp_management_request_logout(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+		pr_notice("ABORT TASK not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+		pr_notice("ABORT TASK SET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+		pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+		pr_notice("TARGET RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	default:
+		pr_notice("unknown management function 0x%x\n",
+			MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+	}
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+		STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+		STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+	/* write the status block back to the initiator */
+	ret = sbp_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		sbp2_pointer_to_addr(&req->orb.status_fifo),
+		&req->status, 8 + status_data_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb status write failed: %x\n", ret);
+		goto out;
+	}
+
+out:
+	fw_card_put(req->card);
+	kfree(req);
+
+	spin_lock_bh(&agent->lock);
+	agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+	spin_unlock_bh(&agent->lock);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_management_agent *agent = callback_data;
+	struct sbp2_pointer *ptr = data;
+	int rcode = RCODE_ADDRESS_ERROR;
+
+	if (!agent->tport->enable)
+		goto out;
+
+	if ((offset != agent->handler.offset) || (length != 8))
+		goto out;
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		struct sbp_management_request *req;
+		int prev_state;
+
+		spin_lock_bh(&agent->lock);
+		prev_state = agent->state;
+		agent->state = MANAGEMENT_AGENT_STATE_BUSY;
+		spin_unlock_bh(&agent->lock);
+
+		if (prev_state == MANAGEMENT_AGENT_STATE_BUSY) {
+			pr_notice("ignoring management request while busy\n");
+			rcode = RCODE_CONFLICT_ERROR;
+			goto out;
+		}
+
+		req = kzalloc(sizeof(*req), GFP_ATOMIC);
+		if (!req) {
+			rcode = RCODE_CONFLICT_ERROR;
+			goto out;
+		}
+
+		req->card = fw_card_get(card);
+		req->generation = generation;
+		req->node_addr = source;
+		req->speed = fw_get_request_speed(request);
+
+		agent->orb_offset = sbp2_pointer_to_addr(ptr);
+		agent->request = req;
+
+		queue_work(system_unbound_wq, &agent->work);
+		rcode = RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+		addr_to_sbp2_pointer(agent->orb_offset, ptr);
+		rcode = RCODE_COMPLETE;
+	} else {
+		rcode = RCODE_TYPE_ERROR;
+	}
+
+out:
+	fw_send_response(card, request, rcode);
+}
+
+struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport)
+{
+	int ret;
+	struct sbp_management_agent *agent;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&agent->lock);
+	agent->tport = tport;
+	agent->handler.length = 0x08;
+	agent->handler.address_callback = sbp_mgt_agent_rw;
+	agent->handler.callback_data = agent;
+	agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+	INIT_WORK(&agent->work, sbp_mgt_agent_process);
+	agent->orb_offset = 0;
+	agent->request = NULL;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+			&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+	fw_core_remove_address_handler(&agent->handler);
+	cancel_work_sync(&agent->work);
+	kfree(agent);
+}
drivers/target/sbp/sbp_management_agent.h new file +34 -0
new file mode 100644
index 0000000..73237ae
@@ -0,0 +1,34 @@
+#ifndef _SBP_MANAGEMENT_AGENT_H
+#define _SBP_MANAGEMENT_AGENT_H
+
+#include <linux/types.h>
+#include <linux/firewire.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "sbp_base.h"
+
+struct sbp_management_agent {
+	spinlock_t lock;
+	struct sbp_tport *tport;
+	struct fw_address_handler handler;
+	int state;
+	struct work_struct work;
+	u64 orb_offset;
+	struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+	struct sbp_management_orb orb;
+	struct sbp_status_block status;
+	struct fw_card *card;
+	int generation;
+	int node_addr;
+	int speed;
+};
+
+struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport);
+void sbp_management_agent_unregister(struct sbp_management_agent *agent);
+
+#endif
Signature · 1 line
1.7.9.5
author maintainer
#82 lore raw

PATCH v2 8/11 firewire-sbp-target: Add sbp_login.{c,h}

In reply to Chris Boot

This file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_login.c |  672 ++++++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_login.h |   17 +
 2 files changed, 689 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_login.c
 create mode 100644 drivers/target/sbp/sbp_login.h
drivers/target/sbp/sbp_login.c new file +672 -0
new file mode 100644
index 0000000..bbcc618
@@ -0,0 +1,672 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kref.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *work);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+	int ret;
+	__be32 high, low;
+
+	ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+			&high, sizeof(high));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+			&low, sizeof(low));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	*guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+	return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+	struct sbp_tpg *tpg, u64 guid)
+{
+	struct se_session *se_sess;
+	struct sbp_session *sess, *found = NULL;
+
+	spin_lock_bh(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		sess = se_sess->fabric_sess_ptr;
+		if (sess->guid == guid)
+			found = sess;
+	}
+	spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+	return found;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+		struct sbp_session *session, struct se_lun *lun)
+{
+	struct sbp_login_descriptor *login, *found = NULL;
+
+	spin_lock_bh(&session->lock);
+	list_for_each_entry(login, &session->login_list, link) {
+		if (login->lun == lun)
+			found = login;
+	}
+	spin_unlock_bh(&session->lock);
+
+	return found;
+}
+
+static int sbp_login_count_all_by_lun(
+		struct sbp_tpg *tpg,
+		struct se_lun *lun,
+		int exclusive)
+{
+	struct se_session *se_sess;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login;
+	int count = 0;
+
+	spin_lock_bh(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		sess = se_sess->fabric_sess_ptr;
+
+		spin_lock_bh(&sess->lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->lun != lun)
+				continue;
+
+			if (!exclusive || login->exclusive)
+				count++;
+		}
+		spin_unlock_bh(&sess->lock);
+	}
+	spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+	return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+	struct sbp_tpg *tpg, int login_id)
+{
+	struct se_session *se_sess;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login, *found = NULL;
+
+	spin_lock_bh(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		sess = se_sess->fabric_sess_ptr;
+
+		spin_lock_bh(&sess->lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->login_id == login_id)
+				found = login;
+		}
+		spin_unlock_bh(&sess->lock);
+	}
+	spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+	return found;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+	struct se_portal_group *se_tpg = &tpg->se_tpg;
+	struct se_lun *se_lun;
+
+	if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+		return ERR_PTR(-EINVAL);
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+	se_lun = se_tpg->tpg_lun_list[lun];
+
+	if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+		se_lun = ERR_PTR(-ENODEV);
+
+	spin_unlock(&se_tpg->tpg_lun_lock);
+
+	return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+		struct sbp_tpg *tpg,
+		u64 guid)
+{
+	struct sbp_session *sess;
+	int ret;
+	char guid_str[17];
+	struct se_node_acl *se_nacl;
+
+	sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+	if (!sess) {
+		pr_err("failed to allocate session descriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sess->se_sess = transport_init_session();
+	if (IS_ERR(sess->se_sess)) {
+		pr_err("failed to init se_session\n");
+
+		ret = PTR_ERR(sess->se_sess);
+		kfree(sess);
+		return ERR_PTR(ret);
+	}
+
+	snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+	se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+	if (!se_nacl) {
+		pr_warn("Node ACL not found for %s\n", guid_str);
+
+		transport_free_session(sess->se_sess);
+		kfree(sess);
+
+		return ERR_PTR(-EPERM);
+	}
+
+	sess->se_sess->se_node_acl = se_nacl;
+
+	spin_lock_init(&sess->lock);
+	INIT_LIST_HEAD(&sess->login_list);
+	INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+
+	sess->guid = guid;
+
+	transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+	return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+	spin_lock_bh(&sess->lock);
+	if (!list_empty(&sess->login_list)) {
+		spin_unlock_bh(&sess->lock);
+		return;
+	}
+	spin_unlock_bh(&sess->lock);
+
+	if (cancel_work)
+		cancel_delayed_work_sync(&sess->maint_work);
+
+	transport_deregister_session_configfs(sess->se_sess);
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->card)
+		fw_card_put(sess->card);
+
+	kfree(sess);
+}
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+	bool cancel_work)
+{
+	struct sbp_session *sess = login->sess;
+
+	/* FIXME: abort/wait on tasks */
+
+	sbp_target_agent_unregister(login->tgt_agt);
+
+	if (sess) {
+		spin_lock_bh(&sess->lock);
+		list_del(&login->link);
+		spin_unlock_bh(&sess->lock);
+
+		sbp_session_release(sess, cancel_work);
+	}
+
+	kfree(login);
+}
+
+void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	struct se_lun *se_lun;
+	int ret;
+	u64 guid;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login;
+	struct sbp_login_response_block *response;
+	int login_response_len;
+
+	se_lun = sbp_get_lun_from_tpg(tpg,
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+	if (IS_ERR(se_lun)) {
+		pr_notice("login to unknown LUN: %d\n",
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+		return;
+	}
+
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+		se_lun->unpacked_lun, guid);
+
+	sess = sbp_session_find_by_guid(tpg, guid);
+	if (sess) {
+		login = sbp_login_find_by_lun(sess, se_lun);
+		if (login) {
+			pr_notice("initiator already logged-in\n");
+
+			/*
+			 * SBP-2 R4 says we should return access denied, but
+			 * that can confuse initiators. Instead we need to
+			 * treat this like a reconnect, but send the login
+			 * response block like a fresh login.
+			 *
+			 * This is required particularly in the case of Apple
+			 * devices booting off the FireWire target, where
+			 * the firmware has an active login to the target. When
+			 * the OS takes control of the session it issues its own
+			 * LOGIN rather than a RECONNECT. To avoid the machine
+			 * waiting until the reconnect_hold expires, we can skip
+			 * the ACCESS_DENIED errors to speed things up.
+			 */
+
+			goto already_logged_in;
+		}
+	}
+
+	/*
+	 * check exclusive bit in login request
+	 * reject with access_denied if any logins present
+	 */
+	if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+			sbp_login_count_all_by_lun(tpg, se_lun, 0)) {
+		pr_warn("refusing exclusive login with other active logins\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check exclusive bit in any existing login descriptor
+	 * reject with access_denied if any exclusive logins present
+	 */
+	if (sbp_login_count_all_by_lun(tpg, se_lun, 1)) {
+		pr_warn("refusing login while another exclusive login present\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check we haven't exceeded the number of allowed logins
+	 * reject with resources_unavailable if we have
+	 */
+	if (sbp_login_count_all_by_lun(tpg, se_lun, 0) >=
+			tport->max_logins_per_lun) {
+		pr_warn("max number of logins reached\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	if (!sess) {
+		sess = sbp_session_create(tpg, guid);
+		if (IS_ERR(sess)) {
+			switch (PTR_ERR(sess)) {
+			case -EPERM:
+				ret = SBP_STATUS_ACCESS_DENIED;
+				break;
+			default:
+				ret = SBP_STATUS_RESOURCES_UNAVAIL;
+				break;
+			}
+
+			req->status.status = cpu_to_be32(
+				STATUS_BLOCK_RESP(
+					STATUS_RESP_REQUEST_COMPLETE) |
+				STATUS_BLOCK_SBP_STATUS(ret));
+			return;
+		}
+
+		sess->node_id = req->node_addr;
+		sess->card = fw_card_get(req->card);
+		sess->generation = req->generation;
+		sess->speed = req->speed;
+
+		schedule_delayed_work(&sess->maint_work,
+				SESSION_MAINTENANCE_INTERVAL);
+	}
+
+	/* only take the latest reconnect_hold into account */
+	sess->reconnect_hold = min(
+		1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+		tport->max_reconnect_timeout) - 1;
+
+	login = kmalloc(sizeof(*login), GFP_KERNEL);
+	if (!login) {
+		pr_err("failed to allocate login descriptor\n");
+
+		sbp_session_release(sess, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login->sess = sess;
+	login->lun = se_lun;
+	login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+	login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+	login->login_id = atomic_inc_return(&login_id);
+
+	login->tgt_agt = sbp_target_agent_register(login);
+	if (IS_ERR(login->tgt_agt)) {
+		ret = PTR_ERR(login->tgt_agt);
+		pr_err("failed to map command block handler: %d\n", ret);
+
+		sbp_session_release(sess, true);
+		kfree(login);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	spin_lock_bh(&sess->lock);
+	list_add_tail(&login->link, &sess->login_list);
+	spin_unlock_bh(&sess->lock);
+
+already_logged_in:
+	response = kzalloc(sizeof(*response), GFP_KERNEL);
+	if (!response) {
+		pr_err("failed to allocate login response block\n");
+
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login_response_len = clamp_val(
+			LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
+			12, sizeof(*response));
+	response->misc = cpu_to_be32(
+		((login_response_len & 0xffff) << 16) |
+		(login->login_id & 0xffff));
+	response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+	addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+		&response->command_block_agent);
+
+	ret = sbp_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		sbp2_pointer_to_addr(&req->orb.ptr2), response,
+		login_response_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("failed to write login response block: %x\n", ret);
+
+		kfree(response);
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	kfree(response);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	pr_notice("QUERY LOGINS not implemented\n");
+	/* FIXME: implement */
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int ret;
+	u64 guid;
+	struct sbp_login_descriptor *login;
+
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+	login = sbp_login_find_by_id(tpg,
+		RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+	if (!login) {
+		pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	if (login->sess->guid != guid) {
+		pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	spin_lock_bh(&login->sess->lock);
+	if (login->sess->card)
+		fw_card_put(login->sess->card);
+
+	/* update the node details */
+	login->sess->generation = req->generation;
+	login->sess->node_id = req->node_addr;
+	login->sess->card = fw_card_get(req->card);
+	login->sess->speed = req->speed;
+	spin_unlock_bh(&login->sess->lock);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int login_id;
+	struct sbp_login_descriptor *login;
+
+	login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+	login = sbp_login_find_by_id(tpg, login_id);
+	if (!login) {
+		pr_warn("cannot find login: %d\n", login_id);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+		return;
+	}
+
+	pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+		login->lun->unpacked_lun, login->login_id);
+
+	if (req->node_addr != login->sess->node_id) {
+		pr_warn("logout from different node ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	sbp_login_release(login, true);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	spin_lock_bh(&sess->lock);
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {
+		pr_info("Waiting for reconnect from node: %016llx\n",
+				sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+
+	spin_unlock_bh(&sess->lock);
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+	struct sbp_login_descriptor *login, *temp;
+	LIST_HEAD(login_list);
+
+	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+	spin_lock_bh(&sess->lock);
+	list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+		login->sess = NULL;
+		list_del(&login->link);
+		list_add_tail(&login->link, &login_list);
+	}
+	spin_unlock_bh(&sess->lock);
+
+	list_for_each_entry_safe(login, temp, &login_list, link) {
+		list_del(&login->link);
+		sbp_login_release(login, false);
+	}
+
+	sbp_session_release(sess, false);
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+	struct sbp_session *sess = container_of(work, struct sbp_session,
+			maint_work.work);
+
+	/* could be called while tearing down the session */
+	spin_lock_bh(&sess->lock);
+	if (list_empty(&sess->login_list)) {
+		spin_unlock_bh(&sess->lock);
+		return;
+	}
+	spin_unlock_bh(&sess->lock);
+
+	if (sess->node_id != -1) {
+		/* check for bus reset and make node_id invalid */
+		session_check_for_reset(sess);
+
+		schedule_delayed_work(&sess->maint_work,
+				SESSION_MAINTENANCE_INTERVAL);
+	} else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+		/* still waiting for reconnect */
+		schedule_delayed_work(&sess->maint_work,
+				SESSION_MAINTENANCE_INTERVAL);
+	} else {
+		/* reconnect timeout has expired */
+		session_reconnect_expired(sess);
+	}
+}
+
drivers/target/sbp/sbp_login.h new file +17 -0
new file mode 100644
index 0000000..3b016f0
@@ -0,0 +1,17 @@
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+
+extern void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+extern void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size);
+
Signature · 1 line
1.7.9.5
#83 lore raw

Re: [PATCH 08/11] firewire-sbp-target: Add sbp_login.{c,h}

Quoted 18 lines · On Apr 11 Chris Boot wrote:
drivers/target/sbp/sbp_login.c new file +18 -0
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	spin_lock_bh(&sess->lock);
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {

drivers/target/sbp/sbp_login.c line 619

Quoted 10 lines
drivers/target/sbp/sbp_login.c new file +10 -0
+		pr_info("Waiting for reconnect from node: %016llx\n",
+				sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+
+	spin_unlock_bh(&sess->lock);
+}

The card->local_node != NULL test by itself is atomic, it does not benefit from being wrapped by card->lock acquisition.

Well, OK, the lock effectively forces the compiler to determine the value of card_valid only once. If the lock weren't there I guess the compiler might feel entitled to reload card->local_node in the second !card_valid test. But even if you lose ACCESS_ONCE behavior by removing the card->lock acquisition, I can't see how that could be detrimental relative to the current code.

I am wondering on the other hand if there isn't actually a dependency between this local_node test and something else, e.g. the generation test. I.e. might the locking be incomplete? Not sure about that. I think I rather want to look at that again when I received the code through mainline.

BTW "card_valid" sounds rather generic; maybe call it "topology_valid"? Either way, it can turn valid or invalid any time when firewire-core gets to handle a self-ID-complete event.

Signature · 3 lines
Stefan Richter
-=====-===-- -=-- -===-
http://arcgraph.de/sr/
author maintainer
#84 lore raw

PATCH v2 9/11 firewire-sbp-target: Add sbp_target_agent.{c,h}

In reply to Chris Boot

This implements the SBP-2 Command Block Agent, or Target Agent. This is what receives SCSI commands and forwards them to the target framework.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_target_agent.c |  395 +++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_target_agent.h |   40 ++++
 2 files changed, 435 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_target_agent.c
 create mode 100644 drivers/target/sbp/sbp_target_agent.h
drivers/target/sbp/sbp_target_agent.c new file +395 -0
new file mode 100644
index 0000000..5fdd401
@@ -0,0 +1,395 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/slab.h>
+
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+#include "sbp_management_agent.h"
+#include "sbp_login.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	__be32 state;
+
+	switch (tcode) {
+	case TCODE_READ_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		spin_lock_bh(&agent->lock);
+		state = cpu_to_be32(agent->state);
+		spin_unlock_bh(&agent->lock);
+		memcpy(data, &state, sizeof(state));
+
+		return RCODE_COMPLETE;
+
+	case TCODE_WRITE_QUADLET_REQUEST:
+		/* ignored */
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_RESET\n");
+		spin_lock_bh(&agent->lock);
+		agent->state = AGENT_STATE_RESET;
+		spin_unlock_bh(&agent->lock);
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:
+		spin_lock_bh(&agent->lock);
+		if (agent->state != AGENT_STATE_SUSPENDED &&
+				agent->state != AGENT_STATE_RESET) {
+			spin_unlock_bh(&agent->lock);
+			pr_notice("Ignoring ORB_POINTER write while active.\n");
+			return RCODE_CONFLICT_ERROR;
+		}
+		agent->state = AGENT_STATE_ACTIVE;
+		spin_unlock_bh(&agent->lock);
+
+		agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+		agent->doorbell = false;
+
+		pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
+				agent->orb_pointer);
+
+		queue_work(system_unbound_wq, &agent->work);
+
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_BLOCK_REQUEST:
+		pr_debug("tgt_agent ORB_POINTER READ\n");
+		spin_lock_bh(&agent->lock);
+		addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+		spin_unlock_bh(&agent->lock);
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		spin_lock_bh(&agent->lock);
+		if (agent->state != AGENT_STATE_SUSPENDED) {
+			spin_unlock_bh(&agent->lock);
+			pr_debug("Ignoring DOORBELL while active.\n");
+			return RCODE_CONFLICT_ERROR;
+		}
+		agent->state = AGENT_STATE_ACTIVE;
+		spin_unlock_bh(&agent->lock);
+
+		agent->doorbell = true;
+
+		pr_debug("tgt_agent DOORBELL\n");
+
+		queue_work(system_unbound_wq, &agent->work);
+
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_QUADLET_REQUEST:
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+		int tcode, void *data, struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+		/* ignored as we don't send unsolicited status */
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_QUADLET_REQUEST:
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static void tgt_agent_rw(struct fw_card *card, struct fw_request *request,
+		int tcode, int destination, int source, int generation,
+		unsigned long long offset, void *data, size_t length,
+		void *callback_data)
+{
+	struct sbp_target_agent *agent = callback_data;
+	struct sbp_session *sess = agent->login->sess;
+	int sess_gen, sess_node, rcode;
+
+	spin_lock_bh(&sess->lock);
+	sess_gen = sess->generation;
+	sess_node = sess->node_id;
+	spin_unlock_bh(&sess->lock);
+
+	if (generation != sess_gen) {
+		pr_notice("ignoring request with wrong generation\n");
+		rcode = RCODE_TYPE_ERROR;
+		goto out;
+	}
+
+	if (source != sess_node) {
+		pr_notice("ignoring request from foreign node (%x != %x)\n",
+				source, sess_node);
+		rcode = RCODE_TYPE_ERROR;
+		goto out;
+	}
+
+	/* turn offset into the offset from the start of the block */
+	offset -= agent->handler.offset;
+
+	if (offset == 0x00 && length == 4) {
+		/* AGENT_STATE */
+		rcode = tgt_agent_rw_agent_state(card, tcode, data, agent);
+	} else if (offset == 0x04 && length == 4) {
+		/* AGENT_RESET */
+		rcode = tgt_agent_rw_agent_reset(card, tcode, data, agent);
+	} else if (offset == 0x08 && length == 8) {
+		/* ORB_POINTER */
+		rcode = tgt_agent_rw_orb_pointer(card, tcode, data, agent);
+	} else if (offset == 0x10 && length == 4) {
+		/* DOORBELL */
+		rcode = tgt_agent_rw_doorbell(card, tcode, data, agent);
+	} else if (offset == 0x14 && length == 4) {
+		/* UNSOLICITED_STATUS_ENABLE */
+		rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+				data, agent);
+	} else {
+		rcode = RCODE_ADDRESS_ERROR;
+	}
+
+out:
+	fw_send_response(card, request, rcode);
+}
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+	struct sbp_target_request *req =
+		container_of(work, struct sbp_target_request, work);
+
+	pr_debug("tgt_orb ptr:0x%llx next_ORB:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+			req->orb_pointer,
+			sbp2_pointer_to_addr(&req->orb.next_orb),
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			be32_to_cpu(req->orb.misc));
+
+	if (req->orb_pointer >> 32)
+		pr_debug("ORB with high bits set\n");
+
+	switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+		case 0:/* Format specified by this standard */
+			sbp_handle_command(req);
+			return;
+		case 1: /* Reserved for future standardization */
+		case 2: /* Vendor-dependent */
+			req->status.status |= cpu_to_be32(
+					STATUS_BLOCK_RESP(
+						STATUS_RESP_REQUEST_COMPLETE) |
+					STATUS_BLOCK_DEAD(0) |
+					STATUS_BLOCK_LEN(1) |
+					STATUS_BLOCK_SBP_STATUS(
+						SBP_STATUS_REQ_TYPE_NOTSUPP));
+			sbp_send_status(req);
+			sbp_free_request(req);
+			return;
+		case 3: /* Dummy ORB */
+			req->status.status |= cpu_to_be32(
+					STATUS_BLOCK_RESP(
+						STATUS_RESP_REQUEST_COMPLETE) |
+					STATUS_BLOCK_DEAD(0) |
+					STATUS_BLOCK_LEN(1) |
+					STATUS_BLOCK_SBP_STATUS(
+						SBP_STATUS_DUMMY_ORB_COMPLETE));
+			sbp_send_status(req);
+			sbp_free_request(req);
+			return;
+		default:
+			BUG();
+	}
+}
+
+/* used to double-check we haven't been issued an AGENT_RESET */
+static inline bool tgt_agent_check_active(struct sbp_target_agent *agent)
+{
+	bool active;
+
+	spin_lock_bh(&agent->lock);
+	active = (agent->state == AGENT_STATE_ACTIVE);
+	spin_unlock_bh(&agent->lock);
+
+	return active;
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+	struct sbp_target_agent *agent =
+		container_of(work, struct sbp_target_agent, work);
+	struct sbp_session *sess = agent->login->sess;
+	struct sbp_target_request *req;
+	int ret;
+	bool doorbell = agent->doorbell;
+	u64 next_orb = agent->orb_pointer;
+
+	while (next_orb && tgt_agent_check_active(agent)) {
+		req = kzalloc(sizeof(*req), GFP_KERNEL);
+		if (!req) {
+			spin_lock_bh(&agent->lock);
+			agent->state = AGENT_STATE_DEAD;
+			spin_unlock_bh(&agent->lock);
+			return;
+		}
+
+		req->login = agent->login;
+		req->orb_pointer = next_orb;
+
+		req->status.status = cpu_to_be32(STATUS_BLOCK_ORB_OFFSET_HIGH(
+					req->orb_pointer >> 32));
+		req->status.orb_low = cpu_to_be32(
+				req->orb_pointer & 0xfffffffc);
+
+		/* read in the ORB */
+		ret = sbp_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+				sess->node_id, sess->generation, sess->speed,
+				req->orb_pointer, &req->orb, sizeof(req->orb));
+		if (ret != RCODE_COMPLETE) {
+			pr_debug("tgt_orb fetch failed: %x\n", ret);
+			req->status.status |= cpu_to_be32(
+					STATUS_BLOCK_SRC(
+						STATUS_SRC_ORB_FINISHED) |
+					STATUS_BLOCK_RESP(
+						STATUS_RESP_TRANSPORT_FAILURE) |
+					STATUS_BLOCK_DEAD(1) |
+					STATUS_BLOCK_LEN(1) |
+					STATUS_BLOCK_SBP_STATUS(
+						SBP_STATUS_UNSPECIFIED_ERROR));
+			spin_lock_bh(&agent->lock);
+			agent->state = AGENT_STATE_DEAD;
+			spin_unlock_bh(&agent->lock);
+
+			sbp_send_status(req);
+			sbp_free_request(req);
+			return;
+		}
+
+		/* check the next_ORB field */
+		if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+			next_orb = 0;
+			req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+						STATUS_SRC_ORB_FINISHED));
+		} else {
+			next_orb = sbp2_pointer_to_addr(&req->orb.next_orb);
+			req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+						STATUS_SRC_ORB_CONTINUING));
+		}
+
+		if (tgt_agent_check_active(agent) && !doorbell) {
+			INIT_WORK(&req->work, tgt_agent_process_work);
+			queue_work(system_unbound_wq, &req->work);
+		} else {
+			/* don't process this request, just check next_ORB */
+			sbp_free_request(req);
+		} 
+
+		spin_lock_bh(&agent->lock);
+		doorbell = agent->doorbell = false;
+
+		/* check if we should carry on processing */
+		if (next_orb)
+			agent->orb_pointer = next_orb;
+		else
+			agent->state = AGENT_STATE_SUSPENDED;
+
+		spin_unlock_bh(&agent->lock);
+	};
+}
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login)
+{
+	struct sbp_target_agent *agent;
+	int ret;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&agent->lock);
+
+	agent->handler.length = 0x20;
+	agent->handler.address_callback = tgt_agent_rw;
+	agent->handler.callback_data = agent;
+
+	agent->login = login;
+	agent->state = AGENT_STATE_RESET;
+	INIT_WORK(&agent->work, tgt_agent_fetch_work);
+	agent->orb_pointer = 0;
+	agent->doorbell = false;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+			&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	fw_core_remove_address_handler(&agent->handler);
+	cancel_work_sync(&agent->work);
+	kfree(agent);
+}
drivers/target/sbp/sbp_target_agent.h new file +40 -0
new file mode 100644
index 0000000..b5d76fd
@@ -0,0 +1,40 @@
+#ifndef _SBP_TARGET_AGENT_H
+#define _SBP_TARGET_AGENT_H
+
+#include <linux/firewire.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <target/target_core_base.h>
+
+#include "sbp_base.h"
+
+struct sbp_target_agent {
+	spinlock_t lock;
+	struct fw_address_handler handler;
+	struct sbp_login_descriptor *login;
+	int state;
+	struct work_struct work;
+	u64 orb_pointer;
+	bool doorbell;
+};
+
+struct sbp_target_request {
+	struct sbp_login_descriptor *login;
+	u64 orb_pointer;
+	struct sbp_command_block_orb orb;
+	struct sbp_status_block status;
+	struct work_struct work;
+
+	struct se_cmd se_cmd;
+	struct sbp_page_table_entry *pg_tbl;
+	void *cmd_buf;
+
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login);
+void sbp_target_agent_unregister(struct sbp_target_agent *agent);
+
+#endif
Signature · 1 line
1.7.9.5
#85 lore raw

Re: [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

Quoted 14 lines · On Apr 11 Chris Boot wrote:
drivers/target/sbp/sbp_target_agent.c new file +14 -0
+static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	__be32 state;
+
+	switch (tcode) {
+	case TCODE_READ_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		spin_lock_bh(&agent->lock);
+		state = cpu_to_be32(agent->state);
+		spin_unlock_bh(&agent->lock);
+		memcpy(data, &state, sizeof(state));
+

drivers/target/sbp/sbp_target_agent.c line 59

Quoted 10 lines
drivers/target/sbp/sbp_target_agent.c new file +10 -0
+		return RCODE_COMPLETE;
+
+	case TCODE_WRITE_QUADLET_REQUEST:
+		/* ignored */
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}

agent->state is an int; reading an int is atomic. Hence the access on its own does not benefit from lock acquisition.

The memcpy can be simplified to

	*(__be32 *)data = cpu_to_be32(agent->state);

if data is always at least quadlet aligned. Thy type cast is only to tell code checkers like sparse that we know what we are doing. If data may be unaligned, you could use

	put_unaligned_be32(agent->state, data);
Signature · 3 lines
Stefan Richter
-=====-===-- -=-- -===-
http://arcgraph.de/sr/
#86 lore raw

Re: [PATCH 09/11] firewire-sbp-target: Add sbp_target_agent.{c,h}

Quoted 20 lines · On Apr 14 Stefan Richter wrote:
Quoted 19 lines · On Apr 11 Chris Boot wrote:
drivers/target/sbp/sbp_target_agent.c new file +19 -0
+static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	__be32 state;
+
+	switch (tcode) {
+	case TCODE_READ_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		spin_lock_bh(&agent->lock);
+		state = cpu_to_be32(agent->state);
+		spin_unlock_bh(&agent->lock);
+		memcpy(data, &state, sizeof(state));
+
+		return RCODE_COMPLETE;
+
+	case TCODE_WRITE_QUADLET_REQUEST:
+		/* ignored */
+		return RCODE_COMPLETE;
Quoted 8 lines

drivers/target/sbp/sbp_target_agent.c line 59

Quoted 5 lines
drivers/target/sbp/sbp_target_agent.c new file +5 -0
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}

agent->state is an int; reading an int is atomic. Hence the access on its own does not benefit from lock acquisition.

Actually this is only true because all write accesses to agent->state are merely assignments (not increments or the like). And I have to admit that I don't remember whether this is only how compilers work in practice or it is actually required by the C language specification.

Quoted 6 lines

The memcpy can be simplified to

	*(__be32 *)data = cpu_to_be32(agent->state);

if data is always at least quadlet aligned. Thy type cast is only to tell code checkers like sparse that we know what we are doing.

So unless there is such an atomicity guarantee, leave the locking as is and prefer

	int state;

	spin_lock_bh(&agent->lock);
	state = agent->state;
	spin_unlock_bh(&agent->lock);

	*(__be32 *)data = cpu_to_be32(state);
Quoted 4 lines

If data may be
unaligned, you could use

	put_unaligned_be32(agent->state, data);

OK, I read further. This is part of your handler.address_callback. data will point to quadlet aligned memory then. It is no where written as an API specification, but you may rest assured that firewire-core will always align quadlet read or block read response buffers at least on quadlet boundary. You can safely cast data into an u32 or __be32 pointer.

Signature · 3 lines
Stefan Richter
-=====-===-- -=-- -===-
http://arcgraph.de/sr/
author maintainer
#87 lore raw

PATCH v2 10/11 firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}

In reply to Chris Boot

Miscellaneous functions for dealing with SCSI commands, status, sense data and data read/write. This is where the real grunt work of pushing data in and out of the FireWire bus happens.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 drivers/target/sbp/sbp_scsi_cmnd.c |  430 ++++++++++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_scsi_cmnd.h |   12 +
 2 files changed, 442 insertions(+)
 create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
 create mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
drivers/target/sbp/sbp_scsi_cmnd.c new file +430 -0
new file mode 100644
index 0000000..13407cf
@@ -0,0 +1,430 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "sbp_base.h"
+#include "sbp_target_agent.h"
+#include "sbp_scsi_cmnd.h"
+
+/*
+ * Simple wrapper around fw_run_transaction that retries the transaction several
+ * times in case of failure, with an exponential backoff.
+ */
+int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
+		int generation, int speed, unsigned long long offset,
+		void *payload, size_t length)
+{
+	int attempt, ret, delay;
+
+	for (attempt = 1; attempt <= 5; attempt++) {
+		ret = fw_run_transaction(card, tcode, destination_id,
+				generation, speed, offset, payload, length);
+
+		switch (ret) {
+		case RCODE_COMPLETE:
+		case RCODE_TYPE_ERROR:
+		case RCODE_ADDRESS_ERROR:
+		case RCODE_GENERATION:
+			return ret;
+
+		default:
+			delay = 5 * attempt * attempt;
+			usleep_range(delay, delay * 2);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Wrapper around sbp_run_transaction that gets the card, destination,
+ * generation and speed out of the request's session.
+ */
+static int sbp_run_request_transaction(struct sbp_target_request *req,
+		int tcode, unsigned long long offset, void *payload,
+		size_t length)
+{
+	struct sbp_login_descriptor *login = req->login;
+	struct sbp_session *sess = login->sess;
+	struct fw_card *card;
+	int node_id, generation, speed, ret;
+
+	spin_lock_bh(&sess->lock);
+	card = fw_card_get(sess->card);
+	node_id = sess->node_id;
+	generation = sess->generation;
+	speed = sess->speed;
+	spin_unlock_bh(&sess->lock);
+
+	ret = sbp_run_transaction(card, tcode, node_id, generation, speed,
+			offset, payload, length);
+
+	fw_card_put(card);
+
+	return ret;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+	int ret, cmd_len, copy_len;
+
+	cmd_len = scsi_command_size(req->orb.command_block);
+
+	req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+	if (!req->cmd_buf)
+		return -ENOMEM;
+
+	memcpy(req->cmd_buf, req->orb.command_block,
+		min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+	if (cmd_len > sizeof(req->orb.command_block)) {
+		pr_debug("sbp_fetch_command: filling in long command\n");
+		copy_len = cmd_len - sizeof(req->orb.command_block);
+
+		ret = sbp_run_request_transaction(req,
+				TCODE_READ_BLOCK_REQUEST,
+				req->orb_pointer + sizeof(req->orb),
+				req->cmd_buf + sizeof(req->orb.command_block),
+				copy_len);
+		if (ret != RCODE_COMPLETE)
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+	int pg_tbl_sz, ret;
+	struct sbp_page_table_entry *pg_tbl;
+
+	if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+		return 0;
+
+	pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+		sizeof(struct sbp_page_table_entry);
+
+	pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+	if (!pg_tbl)
+		return -ENOMEM;
+
+	ret = sbp_run_request_transaction(req, TCODE_READ_BLOCK_REQUEST,
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			pg_tbl, pg_tbl_sz);
+	if (ret != RCODE_COMPLETE) {
+		kfree(pg_tbl);
+		return -EIO;
+	}
+
+	req->pg_tbl = pg_tbl;
+	return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req,
+	u32 *data_len, enum dma_data_direction *data_dir)
+{
+	int data_size, direction, idx;
+
+	data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+	direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+	if (!data_size) {
+		*data_len = 0;
+		*data_dir = DMA_NONE;
+		return;
+	}
+
+	*data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+	if (req->pg_tbl) {
+		*data_len = 0;
+		for (idx = 0; idx < data_size; idx++) {
+			*data_len += be16_to_cpu(
+					req->pg_tbl[idx].segment_length);
+		}
+	} else {
+		*data_len = data_size;
+	}
+}
+
+void sbp_handle_command(struct sbp_target_request *req)
+{
+	struct sbp_login_descriptor *login = req->login;
+	struct sbp_session *sess = login->sess;
+	int ret, unpacked_lun;
+	u32 data_length;
+	enum dma_data_direction data_dir;
+
+	ret = sbp_fetch_command(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	ret = sbp_fetch_page_table(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+			ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	unpacked_lun = req->login->lun->unpacked_lun;
+	sbp_calc_data_length_direction(req, &data_length, &data_dir);
+
+	pr_debug("sbp_handle_command ORB:0x%llx unpacked_lun:%d data_len:%d data_dir:%d\n",
+			req->orb_pointer, unpacked_lun, data_length, data_dir);
+
+	target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+			req->sense_buf, unpacked_lun, data_length,
+			MSG_SIMPLE_TAG, data_dir, 0);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+int sbp_rw_data(struct sbp_target_request *req)
+{
+	struct sbp_session *sess = req->login->sess;
+	int tcode, sg_miter_flags, max_payload, pg_size, speed, node_id,
+		generation, num_pte, length, tfr_length,
+		rcode = RCODE_COMPLETE;
+	struct sbp_page_table_entry *pte;
+	unsigned long long offset;
+	struct fw_card *card;
+	struct sg_mapping_iter iter;
+
+	if (req->se_cmd.data_direction == DMA_FROM_DEVICE) {
+		tcode = TCODE_WRITE_BLOCK_REQUEST;
+		sg_miter_flags = SG_MITER_FROM_SG;
+	} else {
+		tcode = TCODE_READ_BLOCK_REQUEST;
+		sg_miter_flags = SG_MITER_TO_SG;
+	}
+
+	max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+	speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+
+	pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+	if (pg_size) {
+		pr_err("sbp_run_transaction: page size ignored\n");
+		pg_size = 0x100 << pg_size;
+	}
+
+	spin_lock_bh(&sess->lock);
+	card = fw_card_get(sess->card);
+	node_id = sess->node_id;
+	generation = sess->generation;
+	spin_unlock_bh(&sess->lock);
+
+	if (req->pg_tbl) {
+		pte = req->pg_tbl;
+		num_pte = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+		offset = 0;
+		length = 0;
+	} else {
+		pte = NULL;
+		num_pte = 0;
+
+		offset = sbp2_pointer_to_addr(&req->orb.data_descriptor);
+		length = req->se_cmd.data_length;
+	}
+
+	sg_miter_start(&iter, req->se_cmd.t_data_sg, req->se_cmd.t_data_nents,
+		sg_miter_flags);
+
+	while (length || num_pte) {
+		if (!length) {
+			offset = (u64)be16_to_cpu(pte->segment_base_hi) << 32 |
+				be32_to_cpu(pte->segment_base_lo);
+			length = be16_to_cpu(pte->segment_length);
+
+			pte++;
+			num_pte--;
+		}
+
+		sg_miter_next(&iter);
+
+		tfr_length = min3(length, max_payload, (int)iter.length);
+
+		/* FIXME: take page_size into account */
+
+		rcode = sbp_run_transaction(card, tcode, node_id,
+				generation, speed,
+				offset, iter.addr, tfr_length);
+
+		if (rcode != RCODE_COMPLETE)
+			break;
+
+		length -= tfr_length;
+		offset += tfr_length;
+		iter.consumed = tfr_length;
+	}
+
+	sg_miter_stop(&iter);
+	fw_card_put(card);
+
+	if (rcode == RCODE_COMPLETE) {
+		WARN_ON(length != 0);
+		return 0;
+	} else {
+		return -EIO;
+	}
+}
+
+int sbp_send_status(struct sbp_target_request *req)
+{
+	int ret, length;
+	struct sbp_login_descriptor *login = req->login;
+
+	length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+	ret = sbp_run_request_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+			login->status_fifo_addr, &req->status, length);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("sbp_send_status: write failed: 0x%x\n", ret);
+		return -EIO;
+	}
+
+	pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
+			req->orb_pointer);
+
+	return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+	u8 *sense = req->sense_buf;
+	u8 *status = req->status.data;
+
+	WARN_ON(se_cmd->scsi_sense_length < 18);
+
+	switch (sense[0] & 0x7f) { 		/* sfmt */
+	case 0x70: /* current, fixed */
+		status[0] = 0 << 6;
+		break;
+	case 0x71: /* deferred, fixed */
+		status[0] = 1 << 6;
+		break;
+	case 0x72: /* current, descriptor */
+	case 0x73: /* deferred, descriptor */
+	default:
+		/*
+		 * TODO: SBP-3 specifies what we should do with descriptor
+		 * format sense data
+		 */
+		pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+			sense[0]);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+		return;
+	}
+
+	status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+	status[1] =
+		(sense[0] & 0x80) |		/* valid */
+		((sense[2] & 0xe0) >> 1) |	/* mark, eom, ili */
+		(sense[2] & 0x0f);		/* sense_key */
+	status[2] = se_cmd->scsi_asc;		/* sense_code */
+	status[3] = se_cmd->scsi_ascq;		/* sense_qualifier */
+
+	/* information */
+	status[4] = sense[3];
+	status[5] = sense[4];
+	status[6] = sense[5];
+	status[7] = sense[6];
+
+	/* CDB-dependent */
+	status[8] = sense[8];
+	status[9] = sense[9];
+	status[10] = sense[10];
+	status[11] = sense[11];
+
+	/* fru */
+	status[12] = sense[14];
+
+	/* sense_key-dependent */
+	status[13] = sense[15];
+	status[14] = sense[16];
+	status[15] = sense[17];
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_DEAD(0) |
+		STATUS_BLOCK_LEN(5) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+int sbp_send_sense(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+
+	if (se_cmd->scsi_sense_length) {
+		sbp_sense_mangle(req);
+	} else {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+	}
+
+	return sbp_send_status(req);
+}
+
+void sbp_free_request(struct sbp_target_request *req)
+{
+	kfree(req->pg_tbl);
+	kfree(req->cmd_buf);
+	kfree(req);
+}
drivers/target/sbp/sbp_scsi_cmnd.h new file +12 -0
new file mode 100644
index 0000000..d02f4ca
@@ -0,0 +1,12 @@
+
+#include "sbp_target_agent.h"
+
+int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
+		int generation, int speed, unsigned long long offset,
+		void *payload, size_t length);
+
+void sbp_handle_command(struct sbp_target_request *req);
+int sbp_rw_data(struct sbp_target_request *req);
+int sbp_send_status(struct sbp_target_request *req);
+int sbp_send_sense(struct sbp_target_request *req);
+void sbp_free_request(struct sbp_target_request *req);
Signature · 1 line
1.7.9.5
author maintainer
#88 lore raw

PATCH v2 11/11 firewire-sbp-target: Add to target Kconfig and Makefile

In reply to Chris Boot

This commit also adds an entry to the MAINTAINERS file.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Nicholas A. Bellinger <[email protected]>
Cc Stefan Richter <[email protected]>
---
 MAINTAINERS             |    9 +++++++++
 drivers/target/Kconfig  |    1 +
 drivers/target/Makefile |    1 +
 3 files changed, 11 insertions(+)
MAINTAINERS +9 -0
index b31bdff..b084020 100644
@@ -2754,6 +2754,15 @@ T:	git git://git.alsa-project.org/alsa-kernel.git
 S:	Maintained
 F:	sound/firewire/
 
+FIREWIRE SBP-2 TARGET
+M:  Chris Boot <[email protected]>
+L:  [email protected]
+L:  [email protected]
+L:  [email protected]
+T:  git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S:  Maintained
+F:  drivers/target/sbp/
+
 FIREWIRE SUBSYSTEM
 M:	Stefan Richter <[email protected]>
 L:	[email protected]
drivers/target/Kconfig +1 -0
index fc5fa9f..2cfa467 100644
@@ -41,5 +41,6 @@ source "drivers/target/tcm_fc/Kconfig"
 source "drivers/target/iscsi/Kconfig"
 source "drivers/target/tcm_vhost/Kconfig"
 source "drivers/target/usb-gadget/Kconfig"
+source "drivers/target/sbp/Kconfig"
 
 endif
drivers/target/Makefile +1 -0
index 6b5f526..1ae8862 100644
@@ -28,3 +28,4 @@ obj-$(CONFIG_TCM_FC)		+= tcm_fc/
 obj-$(CONFIG_ISCSI_TARGET)	+= iscsi/
 obj-$(CONFIG_TCM_VHOST)		+= tcm_vhost/
 obj-$(CONFIG_TARGET_USB_GADGET)	+= usb-gadget/
+obj-$(CONFIG_FIREWIRE_SBP_TARGET) += sbp/
Signature · 1 line
1.7.9.5
#89 lore raw

Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target

In reply to Chris Boot
Quoted 10 lines · Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Sorry this latest revision has been a long time coming. I was trying to chase down a crashing bug (but haven't been able to replicate it), changed my mind about a large portion of the code twice, and life got in the way as well!

I just want to say this is awesome and I can't wait to try it!

-- Andy

#90 lore raw

Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target

In reply to Chris Boot
Quoted 11 lines · Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Sorry this latest revision has been a long time coming. I was trying to chase down a crashing bug (but haven't been able to replicate it), changed my mind about a large portion of the code twice, and life got in the way as well!

Some very nice work to make this all go Chris.. 8-)

Thanks again btw for your target_core_mod patches along the way to v3!

Quoted 12 lines

Changes in v3:
* Updates for target framework API changes
* Attempt to make headers self-contained
* Remove bad use of atomics and memory barriers
* Rework use of locking
* Use system workqueues
* Wrap fw_run_transaction() to retry failed transactions
* Coding style fixes
* Fix a few bugs
* Overhaul sbp_rw_data()
* Overhaul target fetch agent

So I've merged a squashed version this series from:

https://github.com/bootc/Linux-SBP-2-Target.git patch-v3

into lio-core/master, and have now pushed to k.o upstream

This looks to me like a reasonable candidate for a v3.5 mainline merge, so we need to go ahead and get this into target-pending.. Also, thanks for getting Stefan's ACK's here on the drivers/firmware/ specific changes..

So for new fabric drivers (as discused with Sebastian and usb-gadget recently) that the target team has been enforcing that new fabric modules convert to use a single source / header file for readability and maintainability purposes.

It would be great if you can publish one more branch (no need to re-post the full diff) that takes your patch-v3 down to a single .c/.h file. Otherwise, I'm fine to take the 15 minutes to do this conversion ahead of dropping into target-pending for the sunday night linux-next build.

Thanks again Chris!

--nab

author maintainer
#91 lore raw

Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target

Quoted 7 lines · Nicholas A. Bellinger
Quoted 6 lines · Chris Boot

The FireWire SBP-2 Target is a driver for using an IEEE-1394 connection as a SCSI transport. This module uses the SCSI Target framework to expose LUNs to other machines attached to a FireWire bus, in effect acting as a FireWire hard disk similar to FireWire Target Disk mode on many Apple computers.

Quoted 8 lines
Quoted 5 lines

Sorry this latest revision has been a long time coming. I was trying to chase down a crashing bug (but haven't been able to replicate it), changed my mind about a large portion of the code twice, and life got in the way as well!

Some very nice work to make this all go Chris.. 8-)

Thanks again btw for your target_core_mod patches along the way to v3!

No problem, it's been a very useful learning experience and I'm really glad I did it! :-)

Quoted 12 lines
Quoted 12 lines

Changes in v3:
* Updates for target framework API changes
* Attempt to make headers self-contained
* Remove bad use of atomics and memory barriers
* Rework use of locking
* Use system workqueues
* Wrap fw_run_transaction() to retry failed transactions
* Coding style fixes
* Fix a few bugs
* Overhaul sbp_rw_data()
* Overhaul target fetch agent

Quoted 5 lines

So I've merged a squashed version this series from:

https://github.com/bootc/Linux-SBP-2-Target.git patch-v3

into lio-core/master, and have now pushed to k.o upstream

Thanks!

Quoted 10 lines

This looks to me like a reasonable candidate for a v3.5 mainline merge, so we need to go ahead and get this into target-pending.. Also, thanks for getting Stefan's ACK's here on the drivers/firmware/ specific changes..

So for new fabric drivers (as discused with Sebastian and usb-gadget recently) that the target team has been enforcing that new fabric modules convert to use a single source / header file for readability and maintainability purposes.

Quoted 4 lines

It would be great if you can publish one more branch (no need to re-post the full diff) that takes your patch-v3 down to a single .c/.h file. Otherwise, I'm fine to take the 15 minutes to do this conversion ahead of dropping into target-pending for the sunday night linux-next build.

I'll get onto it over the weekend, I hope. I assume in this case I just stick it all in drivers/target/ instead of having a sbp/ directory underneath that?

Cheers,
Chris

#92 lore raw

Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target

Quoted 1 line · Chris Boot

On 13/04/2012 04:03, Nicholas A. Bellinger wrote:

<SNIP>

Quoted 18 lines
Quoted 5 lines

So I've merged a squashed version this series from:

https://github.com/bootc/Linux-SBP-2-Target.git patch-v3

into lio-core/master, and have now pushed to k.o upstream

Thanks!

Quoted 10 lines

This looks to me like a reasonable candidate for a v3.5 mainline merge, so we need to go ahead and get this into target-pending.. Also, thanks for getting Stefan's ACK's here on the drivers/firmware/ specific changes..

So for new fabric drivers (as discused with Sebastian and usb-gadget recently) that the target team has been enforcing that new fabric modules convert to use a single source / header file for readability and maintainability purposes.

Quoted 9 lines
Quoted 4 lines

It would be great if you can publish one more branch (no need to re-post the full diff) that takes your patch-v3 down to a single .c/.h file. Otherwise, I'm fine to take the 15 minutes to do this conversion ahead of dropping into target-pending for the sunday night linux-next build.

I'll get onto it over the weekend, I hope. I assume in this case I just stick it all in drivers/target/ instead of having a sbp/ directory underneath that?

I was eager to see this change, so I decided to go ahead and do the mechanical conversion myself in the following branch:

git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core.git sbp-merge

As mentioned, it converts drivers/target/sbp/ to use a single sbp_target.[c,h] source/header file following mainline convention for new target fabric drivers. Also make as many functions as possible statically defined and add inline prototypes where necessary.

This patch also renames the sbp-target config entry from:

    FIREWIRE_SBP_TARGET -> SBP_TARGET

and also renames the final generated module name from:

    fireware-sbp-target.ko -> sbp_target.ko

Here is what the diffstat ends up looking like:

 drivers/target/Makefile                   |    2 +-
 drivers/target/sbp/Kconfig                |    6 +-
 drivers/target/sbp/Makefile               |   12 +-
 drivers/target/sbp/sbp_base.h             |  211 ---
 drivers/target/sbp/sbp_configfs.c         |  737 --------
 drivers/target/sbp/sbp_fabric.c           |  314 ----
 drivers/target/sbp/sbp_fabric.h           |   40 -
 drivers/target/sbp/sbp_login.c            |  672 --------
 drivers/target/sbp/sbp_login.h            |   17 -
 drivers/target/sbp/sbp_management_agent.c |  255 ---
 drivers/target/sbp/sbp_management_agent.h |   34 -
 drivers/target/sbp/sbp_scsi_cmnd.c        |  430 -----
 drivers/target/sbp/sbp_scsi_cmnd.h        |   12 -
 drivers/target/sbp/sbp_target.c           | 2621 +++++++++++++++++++++++++++++
 drivers/target/sbp/sbp_target.h           |  254 +++
 drivers/target/sbp/sbp_target_agent.c     |  395 -----
 drivers/target/sbp/sbp_target_agent.h     |   40 -
 17 files changed, 2879 insertions(+), 3173 deletions(-)
 delete mode 100644 drivers/target/sbp/sbp_base.h
 delete mode 100644 drivers/target/sbp/sbp_configfs.c
 delete mode 100644 drivers/target/sbp/sbp_fabric.c
 delete mode 100644 drivers/target/sbp/sbp_fabric.h
 delete mode 100644 drivers/target/sbp/sbp_login.c
 delete mode 100644 drivers/target/sbp/sbp_login.h
 delete mode 100644 drivers/target/sbp/sbp_management_agent.c
 delete mode 100644 drivers/target/sbp/sbp_management_agent.h
 delete mode 100644 drivers/target/sbp/sbp_scsi_cmnd.c
 delete mode 100644 drivers/target/sbp/sbp_scsi_cmnd.h
 create mode 100644 drivers/target/sbp/sbp_target.c
 create mode 100644 drivers/target/sbp/sbp_target.h
 delete mode 100644 drivers/target/sbp/sbp_target_agent.c
 delete mode 100644 drivers/target/sbp/sbp_target_agent.h

Please have a look at this branch over the next days, and if you're OK with the re-org changes I'll go ahead and merge this into lio-core/master, and drop into target-pending for sunday night's build.

Thanks Chris!

--nab

author maintainer
#93 lore raw

cover sbp-target: cleanup after merge into single file

Thanks to Nick Bellinger for doing the work for me and merging the sbp-target code into single source/header files. This little series does a bit of cleanup and updates the TODO file.

I'll also take this opportunity to add my sign-off to Nick's patch that merges everything together, which this series is on top of:

Cheers,
Chris

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Stefan Richter <[email protected]>
Cc Nicholas Bellinger <[email protected]>

Chris Boot (2):
sbp-target: minor cleanups after merging into single file
sbp-target: update TODO file

 drivers/target/sbp/TODO         |    8 ++++----
 drivers/target/sbp/sbp_target.c |    6 +++---
 drivers/target/sbp/sbp_target.h |    3 ---
 3 files changed, 7 insertions(+), 10 deletions(-)
Signature · 1 line
1.7.9.5
author maintainer
#94 lore raw

PATCH 1/2 sbp-target: minor cleanups after merging into single file

Just a few minor cleanups to make a couple more elements static and remove some extern definitions from the header which are not exported.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Stefan Richter <[email protected]>
Cc Nicholas Bellinger <[email protected]>
---
 drivers/target/sbp/sbp_target.c |    6 +++---
 drivers/target/sbp/sbp_target.h |    3 ---
 2 files changed, 3 insertions(+), 6 deletions(-)
drivers/target/sbp/sbp_target.c +3 -3
index d7c9ac9..37c6098 100644
@@ -43,10 +43,10 @@
 #include "sbp_target.h"
 
 /* Local pointer to allocated TCM configfs fabric module */
-struct target_fabric_configfs *sbp_fabric_configfs;
+static struct target_fabric_configfs *sbp_fabric_configfs;
 
 /* FireWire address region for management and command block address handlers */
-const struct fw_address_region sbp_register_region = {
+static const struct fw_address_region sbp_register_region = {
 	.start	= CSR_REGISTER_BASE + 0x10000,
 	.end	= 0x1000000000000ULL,
 };
@@ -1435,7 +1435,7 @@ static void sbp_sense_mangle(struct sbp_target_request *req)
 		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
 }
 
-int sbp_send_sense(struct sbp_target_request *req)
+static int sbp_send_sense(struct sbp_target_request *req)
 {
 	struct se_cmd *se_cmd = &req->se_cmd;
 
drivers/target/sbp/sbp_target.h +0 -3
index 1ec2269..6d0d74a 100644
@@ -193,9 +193,6 @@ struct sbp_tport {
 	int max_logins_per_lun;
 };
 
-extern struct target_fabric_configfs *sbp_fabric_configfs;
-extern const struct fw_address_region sbp_register_region;
-
 static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
 {
 	return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
Signature · 1 line
1.7.9.5
author maintainer
#95 lore raw

PATCH 2/2 sbp-target: update TODO file

In reply to Chris Boot

The TODO wasn't updated after the last batch of changes before the merge, so remove those items that have been completed. This patch also re-orders the existing items in priority order and adds a new one.

Cc Andy Grover <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc Stefan Richter <[email protected]>
Cc Nicholas Bellinger <[email protected]>
---
 drivers/target/sbp/TODO |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/target/sbp/TODO +4 -4
index eaec1c9..e800058 100644
@@ -1,7 +1,7 @@
-* Force-terminate sessions when disabling targets
+1. Improve FireWire data transfer rate by queuing up several simultaneous transfers
+2. Force-terminate sessions when disabling targets
+3. Implement TASK MANAGEMENT functionality
 * Ability to have several SCSI commands in-flight (TCQ?)
-* Retry failed FireWire transactions a few times with exponential backoff
-* Take into account the page_size field for transfers and/or page tables
+* Take into account the page_size field for transfers
 * Handle descriptor format sense data
 * Implement QUERY LOGINS management ORB
-* Implement TASK MANAGEMENT functionality
Signature · 1 line
1.7.9.5
#96 lore raw

Re: [PATCH 0/2] sbp-target: cleanup after merge into single file

In reply to Chris Boot
Quoted 9 lines · Chris Boot

Thanks to Nick Bellinger for doing the work for me and merging the sbp-target code into single source/header files. This little series does a bit of cleanup and updates the TODO file.

I'll also take this opportunity to add my sign-off to Nick's patch that merges everything together, which this series is on top of:

Thanks for the Ack. ;)

I'll go ahead and merge the sbp-merge branch back into lio-core/master now, and apply this series on top of master.

Also, it looks like Stefan has a few more comments from the last round of review.. I'll go ahead and get the latest HEAD prepped for target-pending today, and plan to rebase any forthcoming incremental patches from you into a single merge commit in target-pending.

Thanks Chris!

--nab

#97 lore raw

Re: [PATCH 0/2] sbp-target: cleanup after merge into single file

Quoted 2 lines · On Apr 14 Nicholas A. Bellinger wrote:

Also, it looks like Stefan has a few more comments from the last round of review..

Don't consider them blocking. I would welcome it if the code gets merged soon, it looks very nice and maintainable. The card state <-> session state interactions which I was wondering about will be more comfortable for me to investigate when it is in mainline; I believe there is no urgency for me to look into that. Other obscure but simple things like making use of memory alignment guarantees from firewire-core are more effectively addressed if I can send patches rather than comments.

Signature · 3 lines
Stefan Richter
-=====-===-- -=-- -====
http://arcgraph.de/sr/
#98 lore raw

Re: [PATCH 0/2] sbp-target: cleanup after merge into single file

Quoted 11 lines · Stefan Richter
Quoted 2 lines · On Apr 14 Nicholas A. Bellinger wrote:

Also, it looks like Stefan has a few more comments from the last round of review..

Don't consider them blocking. I would welcome it if the code gets merged soon, it looks very nice and maintainable. The card state <-> session state interactions which I was wondering about will be more comfortable for me to investigate when it is in mainline; I believe there is no urgency for me to look into that. Other obscure but simple things like making use of memory alignment guarantees from firewire-core are more effectively addressed if I can send patches rather than comments.

<nod> In that case, I'll go ahead and add your Acked-by to the initial merge commit for sbp-target that will be appearing in target-pending/for-next-merge shortly.

Thanks Stefan!

--nab