FireWire/SBP2 Target mode
Subsystems (4)
Chris Boot
maintainerHi 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
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
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/
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
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
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
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
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
Chris Boot
maintainerHi 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]
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_handlercallback
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
Chris Boot
maintainerQuoted 8 lines · Clemens Ladisch
Quoted 2 lines · Chris Boot
You can pull the code from:
git://github.com/bootc/Linux-SBP-2-Target.gitThe TODO file says:
Quoted 1 line
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat 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]
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.gitThe TODO file says:
Quoted 1 line
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat is the speed needed for?
Quoted 8 lines
"The speed at which the block write request to the
MANAGEMENT_AGENTregister 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’sstatus_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_sbp2is 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/
Chris Boot
maintainerQuoted 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.gitThe TODO file says:
Quoted 1 line
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat is the speed needed for?
Quoted 8 lines
"The speed at which the block write request to the
MANAGEMENT_AGENTregister 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’sstatus_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 anfw_deviceby 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_devicelookup, 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_payloadgiven 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 themax_payloadgiven 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_sbp2is 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]
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.gitThe TODO file says:
Quoted 1 line
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat is the speed needed for?
Quoted 8 lines
"The speed at which the block write request to the
MANAGEMENT_AGENTregister 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’sstatus_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 anfw_deviceby 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_requestis opaque. The value is easily available fromrequest->response.speedand 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_nodeis 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/
Chris Boot
maintainerQuoted 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.gitThe TODO file says:
Quoted 1 line
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat is the speed needed for?
Quoted 8 lines
"The speed at which the block write request to the
MANAGEMENT_AGENTregister 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’sstatus_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 anfw_deviceby 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_requestis opaque. The value is easily available fromrequest->response.speedand 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_nodeis 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]
Chris Boot
maintainerQuoted 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.gitThe TODO file says:
Quoted 1 line
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat is the speed needed for?
Quoted 8 lines
"The speed at which the block write request to the
MANAGEMENT_AGENTregister 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’sstatus_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 anfw_deviceby 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_requestis opaque. The value is easily available fromrequest->response.speedand 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_nodeis 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_handlercallback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must callfw_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
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.gitQuoted 7 lines
The TODO file says:
Quoted 3 lines
* Update Juju so we can get the speed in the
fw_address_handlercallbackWhat is the speed needed for?
"The speed at which the block write request to the
MANAGEMENT_AGENTregister 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’sstatus_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 anfw_deviceby 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_requestis opaque. The value is easily available fromrequest->response.speedand 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_nodeis 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_handlercallback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must callfw_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_handlerbut 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_nodeis 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 thefw_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/
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_nodeis 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_handlercallback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must callfw_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_handlerbut 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/
Chris Boot
maintainerQuoted 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_handlercallback is called in interrupt context, and I can't sleep from within there. As far as I'm aware I must callfw_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_handlerbut 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 theaddress_callback(). But there is little reason not to.
Quoted 6 lines
fw_send_response()ends the lifetime of anfw_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]
Chris Boot
maintainercover firewire-sbp-target: FireWire SBP-2 SCSI target
In reply to Chris BootHi 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]
Chris Boot
maintainerPATCH 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 2/13 firewire: Add EXPORT_SYMBOL_GPL(fw_card_release)
In reply to Chris BootThe 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.
--- drivers/firewire/core-card.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
drivers/firewire/core-card.c +1 -0
@@ -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
Chris Boot
maintainerPATCH 3/13 firewire-sbp-target: Add Kconfig, Makefile and TODO
In reply to Chris BootThe 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.
--- 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
@@ -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
@@ -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
@@ -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
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.
Signed-off-by Chris Boot <[email protected]>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/TODOdrivers/target/sbp/Kconfig new file +4
-0@@ -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
Chris Boot
maintainerPATCH 4/13 firewire-sbp-target: Add sbp_base.h header
In reply to Chris BootThis 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.
--- 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
@@ -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
Chris Boot
maintainerPATCH 5/13 firewire-sbp-target: Add sbp_configfs.c
In reply to Chris BootThis 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.
--- 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
@@ -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
Chris Boot
maintainerPATCH 6/13 firewire-sbp-target: Add sbp_fabric.{c,h}
In reply to Chris BootThis serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.
--- 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
@@ -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
@@ -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
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.
Signed-off-by Chris Boot <[email protected]>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.hdrivers/target/sbp/sbp_fabric.c new file +20
-0@@ -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]
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_TASKand 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/
Re: [PATCH 06/13] firewire-sbp-target: Add sbp_fabric.{c,h}
In reply to a message not in the archiveQuoted 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.
Signed-off-by Chris Boot <[email protected]>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.ccreate mode 100644drivers/target/sbp/sbp_fabric.hdrivers/target/sbp/sbp_fabric.c new file +0
-0<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 callingTFO->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_mapto 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_TASKTMR patches useTFO->get_task_tag()to locate a referenced tag to locate these_cmddescriptor. Since we don't support TMRs yet in sbp, this value will only be used forinformational
Quoted 8 lines
Quoted 1 line
purposes.
I'd like to support
ABORT_TASKand 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
Chris Boot
maintainerPATCH 7/13 firewire-sbp-target: Add sbp_proto.{c,h}
In reply to Chris BootThese are functions to generate TransportID identifiers as per SPC-4 revision 17.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 8/13 firewire-sbp-target: add sbp_management_agent.{c,h}
In reply to Chris BootThis 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.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 9/13 firewire-sbp-target: Add sbp_login.{c,h}
In reply to Chris BootThis file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 10/13 firewire-sbp-target: Add sbp_target_agent.{c,h}
In reply to Chris BootThis implements the SBP-2 Command Block Agent, or Target Agent. This is what receives SCSI commands and forwards them to the target framework.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 11/13 firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}
In reply to Chris BootMiscellaneous 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.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 12/13 firewire-sbp-target: Add sbp_util.{c,h}
In reply to Chris BootCommon helper functions and global declarations.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 13/13 firewire-sbp-target: Add to target Kconfig and Makefile
In reply to Chris BootThis commit also adds an entry to the MAINTAINERS file.
--- MAINTAINERS | 9 +++++++++ drivers/target/Kconfig | 1 + drivers/target/Makefile | 1 + 3 files changed, 11 insertions(+), 0 deletions(-)
MAINTAINERS +9 -0
@@ -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
@@ -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
@@ -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
Re: [RFC][PATCH 00/13] firewire-sbp-target: FireWire SBP-2 SCSI target
In reply to Chris BootQuoted 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/
Chris Boot
maintainerRe: [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 ProtocolThough 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_SYMBOLsinsbp_proto.ccan 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]
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 bepr_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/
Chris Boot
maintainerv2 cover firewire-sbp-target: FireWire SBP-2 SCSI target
In reply to Chris BootThe 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].
Chris Boot
maintainerPATCH 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
@@ -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
@@ -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
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/
Chris Boot
maintainerRe: [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]
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/
Chris Boot
maintainerRe: [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]
Chris Boot
maintainerPATCH v2 2/11 firewire: Move fw_card kref functions into linux/firewire.h
In reply to Chris BootWhen 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
@@ -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
@@ -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
@@ -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
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_deviceobjects (e.g. it only publishes FireWire units and doesn't subscribe to them), you likely need to keep referenced to structfw_cardobjects so that you can send messages to other nodes. This patch movesfw_card_put(),fw_card_get()andfw_card_release()into the publicinclude/linux/firewire.hheader instead ofdrivers/firewire/core.h, and addsEXPORT_SYMBOL_GPL(fw_card_release).
Quoted 6 lines
The firewire-sbp-target module requires these so it can keep a reference to the
fw_cardobject in order that it can fetch ORBs to execute and read/write related data and status information.Signed-off-by Chris Boot <[email protected]>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@@ -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@@ -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@@ -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/
Chris Boot
maintainerRe: [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_deviceobjects (e.g. it only publishes FireWire units and doesn't subscribe to them), you likely need to keep referenced to structfw_cardobjects so that you can send messages to other nodes. This patch movesfw_card_put(),fw_card_get()andfw_card_release()into the publicinclude/linux/firewire.hheader instead ofdrivers/firewire/core.h, and addsEXPORT_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_cardobject in order that it can fetch ORBs to execute and read/write related data and status information.Signed-off-by Chris Boot<[email protected]>Cc Clemens Ladisch<[email protected]>Acked-by Stefan Richter<[email protected]>
Thanks!
[snip]
Cheers,
Chris
Signature · 2 lines
Chris Boot [email protected]
Chris Boot
maintainerPATCH v2 3/11 firewire-sbp-target: Add Kconfig, Makefile and TODO
In reply to Chris BootThe 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.
--- 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
@@ -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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 4/11 firewire-sbp-target: Add sbp_base.h header
In reply to Chris BootThis 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.
--- 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
@@ -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
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/
Chris Boot
maintainerRe: [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, structse_wwn, struct workqueue and so on.
Thanks. I'll work on fixing this.
Cheers,
Chris
Signature · 2 lines
Chris Boot [email protected]
Chris Boot
maintainerPATCH v2 5/11 firewire-sbp-target: Add sbp_configfs.c
In reply to Chris BootThis 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.
--- 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
@@ -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
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/
Chris Boot
maintainerRe: [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]
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/
Chris Boot
maintainerPATCH v2 6/11 firewire-sbp-target: Add sbp_fabric.{c,h}
In reply to Chris BootThis serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.
--- 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
@@ -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
Chris Boot
maintainerPATCH v2 7/11 firewire-sbp-target: add sbp_management_agent.{c,h}
In reply to Chris BootThis 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.
--- 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
@@ -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
@@ -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
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/
Chris Boot
maintainerRe: [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->stateaccess. 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->statechange 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]
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/
Chris Boot
maintainerPATCH v2 8/11 firewire-sbp-target: Add sbp_login.{c,h}
In reply to Chris BootThis file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.
--- 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
@@ -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
@@ -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
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.
Signed-off-by Chris Boot <[email protected]>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.hdrivers/target/sbp/sbp_login.c new file +73
-0@@ -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/
Chris Boot
maintainerRe: [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.
Signed-off-by Chris Boot<[email protected]>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.ccreate mode 100644drivers/target/sbp/sbp_login.hdrivers/target/sbp/sbp_login.c new file +73
-0@@ -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(
structsbp_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(
structsbp_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_listright 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_cardinternals, 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 ofsess->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]
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_idagainst 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/
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/
Chris Boot
maintainerPATCH v2 9/11 firewire-sbp-target: Add sbp_target_agent.{c,h}
In reply to Chris BootThis implements the SBP-2 Command Block Agent, or Target Agent. This is what receives SCSI commands and forwards them to the target framework.
--- 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
@@ -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
@@ -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
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/
Chris Boot
maintainerRe: [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->stateis 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]
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->stateis 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/
Chris Boot
maintainerRe: [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->stateis 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]
Chris Boot
maintainerPATCH v2 10/11 firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}
In reply to Chris BootMiscellaneous 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.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 11/11 firewire-sbp-target: Add to target Kconfig and Makefile
In reply to Chris BootThis commit also adds an entry to the MAINTAINERS file.
--- MAINTAINERS | 9 +++++++++ drivers/target/Kconfig | 1 + drivers/target/Makefile | 1 + 3 files changed, 11 insertions(+), 0 deletions(-)
MAINTAINERS +9 -0
@@ -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
@@ -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
@@ -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
Chris Boot
maintainerv3 cover firewire-sbp-target: FireWire SBP-2 SCSI target
In reply to Chris BootThe 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
Chris Boot
maintainerPATCH 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 2/11 firewire: Move fw_card kref functions into linux/firewire.h
In reply to Chris BootWhen 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
@@ -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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 3/11 firewire-sbp-target: Add Kconfig, Makefile and TODO
In reply to Chris BootThe 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.
--- 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
@@ -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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 4/11 firewire-sbp-target: Add sbp_base.h header
In reply to Chris BootThis 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.
--- 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
@@ -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
Chris Boot
maintainerPATCH v2 5/11 firewire-sbp-target: Add sbp_configfs.c
In reply to Chris BootThis 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.
--- 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
@@ -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
Chris Boot
maintainerPATCH v2 6/11 firewire-sbp-target: Add sbp_fabric.{c,h}
In reply to Chris BootThis serves as further glue between the target framework and SBP-2, in this case dealing with SCSI command submission and data in/out.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 7/11 firewire-sbp-target: Add sbp_management_agent.{c,h}
In reply to Chris BootThis 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.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 8/11 firewire-sbp-target: Add sbp_login.{c,h}
In reply to Chris BootThis file contains the implementation of the login, reconnect and logout management ORBs in SBP-2.
--- 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
@@ -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
@@ -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
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/
Chris Boot
maintainerPATCH v2 9/11 firewire-sbp-target: Add sbp_target_agent.{c,h}
In reply to Chris BootThis implements the SBP-2 Command Block Agent, or Target Agent. This is what receives SCSI commands and forwards them to the target framework.
--- 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
@@ -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
@@ -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
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/
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->stateis 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 useput_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/
Chris Boot
maintainerPATCH v2 10/11 firewire-sbp-target: Add sbp_scsi_cmnd.{c,h}
In reply to Chris BootMiscellaneous 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.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH v2 11/11 firewire-sbp-target: Add to target Kconfig and Makefile
In reply to Chris BootThis commit also adds an entry to the MAINTAINERS file.
--- MAINTAINERS | 9 +++++++++ drivers/target/Kconfig | 1 + drivers/target/Makefile | 1 + 3 files changed, 11 insertions(+)
MAINTAINERS +9 -0
@@ -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
@@ -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
@@ -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
Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
In reply to Chris BootQuoted 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
Re: [PATCH v3 00/11] firewire-sbp-target: FireWire SBP-2 SCSI target
In reply to Chris BootQuoted 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
* Wrapfw_run_transaction()to retry failed transactions
* Coding style fixes
* Fix a few bugs
* Overhaulsbp_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
Chris Boot
maintainerRe: [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_modpatches 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
* Wrapfw_run_transaction()to retry failed transactions
* Coding style fixes
* Fix a few bugs
* Overhaulsbp_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
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
Chris Boot
maintainercover 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
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
Chris Boot
maintainerPATCH 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.
--- 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
@@ -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
@@ -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
Chris Boot
maintainerPATCH 2/2 sbp-target: update TODO file
In reply to Chris BootThe 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.
--- drivers/target/sbp/TODO | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/target/sbp/TODO +4 -4
@@ -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
Re: [PATCH 0/2] sbp-target: cleanup after merge into single file
In reply to Chris BootQuoted 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:
Acked-by Chris Boot <[email protected]>
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
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/
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