Merged revisions 5875,5878-5895,5903-5905,5910,5912-5914,5928-5929,5931-5991 via svnmerge from

svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk

........
  r5875 | bvassche | 2014-11-16 19:58:07 +0200 (Sun, 16 Nov 2014) | 1 line
  
  nightly build: Update kernel versions
........
  r5878 | bvassche | 2014-11-19 02:17:41 +0200 (Wed, 19 Nov 2014) | 1 line
  
  srpt/Makefile: Add double quotes around a path
........
  r5879 | bvassche | 2014-11-19 02:20:20 +0200 (Wed, 19 Nov 2014) | 1 line
  
  scripts/generate-release-archive: Accept an optional list of file names
........
  r5880 | bvassche | 2014-11-22 13:12:29 +0200 (Sat, 22 Nov 2014) | 1 line
  
  nightly build: Update kernel versions
........
  r5881 | bvassche | 2014-11-24 19:59:14 +0200 (Mon, 24 Nov 2014) | 4 lines
  
  ib_srpt: Add support for HCA's that do not support SRQ
  
  Based on a patch provided by Parav Pandit <Parav.Pandit@Emulex.Com>
........
  r5882 | vlnb | 2014-11-26 09:02:17 +0200 (Wed, 26 Nov 2014) | 3 lines
  
  Update for kernels 3.17.x
........
  r5883 | bvassche | 2014-11-26 10:05:09 +0200 (Wed, 26 Nov 2014) | 1 line
  
  Add kernel 3.17 build infrastructure
........
  r5884 | bvassche | 2014-11-26 10:07:08 +0200 (Wed, 26 Nov 2014) | 1 line
  
  nightly build: Add kernel 3.17
........
  r5885 | bvassche | 2014-11-26 10:16:44 +0200 (Wed, 26 Nov 2014) | 6 lines
  
  Fix kernel 3.17 checkpatch warnings about 'long long unsigned'
  
  Avoid that checkpatch reports the following warning:
  
  WARNING: type 'long long unsigned' should be specified in 'unsigned long long' order.
........
  r5886 | bvassche | 2014-11-26 15:38:52 +0200 (Wed, 26 Nov 2014) | 1 line
  
  Build fixes for RHEL 6.6 kernel 2.6.32-504
........
  r5887 | bvassche | 2014-11-26 16:39:51 +0200 (Wed, 26 Nov 2014) | 1 line
  
  ib_srpt: Make the send queue full messages more informational
........
  r5888 | bvassche | 2014-11-26 18:25:57 +0200 (Wed, 26 Nov 2014) | 1 line
  
  scripts/specialize-patch: Support blanks around numbers inside parentheses
........
  r5889 | bvassche | 2014-11-26 21:42:10 +0200 (Wed, 26 Nov 2014) | 1 line
  
  scripts/specialize-patch: Reduce noise in nightly build output
........
  r5890 | vlnb | 2014-11-27 06:36:33 +0200 (Thu, 27 Nov 2014) | 3 lines
  
  Cleanup
........
  r5891 | bvassche | 2014-11-27 17:18:58 +0200 (Thu, 27 Nov 2014) | 1 line
  
  scst.h: Add uintptr_t
........
  r5892 | bvassche | 2014-11-27 17:19:21 +0200 (Thu, 27 Nov 2014) | 1 line
  
  ib_srpt: Add support for immediate data
........
  r5893 | bvassche | 2014-11-27 17:24:17 +0200 (Thu, 27 Nov 2014) | 1 line
  
  ib_srpt: Log reject reason
........
  r5894 | bvassche | 2014-11-27 17:29:29 +0200 (Thu, 27 Nov 2014) | 1 line
  
  ib_srpt: Rework the max_sge computation changes from r5795
........
  r5895 | bvassche | 2014-11-28 11:16:37 +0200 (Fri, 28 Nov 2014) | 1 line
  
  scst: Add scripts/rebuild-rhel-kernel-rpm to the SCST release archive
........
  r5903 | bvassche | 2014-12-03 13:50:06 +0200 (Wed, 03 Dec 2014) | 4 lines
  
  scripts/rebuild-rhel-kernel-rpm: Fix an error message
  
  Reported-by: Hiroyuki Sato <hiroysato@gmail.com>
........
  r5904 | bvassche | 2014-12-03 19:06:57 +0200 (Wed, 03 Dec 2014) | 1 line
  
  iscsi-scst/kernel/patches/rhel/put_page_callback-2.6.32-504.patch: Add
........
  r5905 | bvassche | 2014-12-03 19:07:31 +0200 (Wed, 03 Dec 2014) | 1 line
  
  scripts/rebuild-rhel-kernel-rpm: Add support for RHEL 6.6
........
  r5910 | bvassche | 2014-12-04 13:50:58 +0200 (Thu, 04 Dec 2014) | 1 line
  
  scripts/generate-kernel-patch: Swap two filters
........
  r5912 | bvassche | 2014-12-04 14:19:56 +0200 (Thu, 04 Dec 2014) | 4 lines
  
  /etc/init.d/scst: Exit with status code 0 upon 'start' if already running
  
  Reported-by: Dimitar Tanev <dimitar@linuxdevgroup.org>
........
  r5913 | vlnb | 2014-12-05 01:41:52 +0200 (Fri, 05 Dec 2014) | 3 lines
  
  FORMAT commands should be strictly serialized
........
  r5914 | vlnb | 2014-12-05 01:43:51 +0200 (Fri, 05 Dec 2014) | 3 lines
  
  Oops, fix for the previous commit
........
  r5928 | vlnb | 2014-12-06 07:02:27 +0200 (Sat, 06 Dec 2014) | 3 lines
  
  Web updates
........
  r5929 | bvassche | 2014-12-09 14:33:16 +0200 (Tue, 09 Dec 2014) | 1 line
  
  rpm build: Add support for qla2x00t driver in QLogic git repository
........
  r5931 | vlnb | 2014-12-11 06:27:17 +0200 (Thu, 11 Dec 2014) | 3 lines
  
  Docs update
........
  r5932 | vlnb | 2014-12-11 06:34:36 +0200 (Thu, 11 Dec 2014) | 8 lines
  
  scst_vdisk: Increase virtual device name length
  
  This change makes integration with OpenStack easier since OpenStack GUIDs
  are 36 characters long: 32 hex characters and four dashes.
  
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5933 | vlnb | 2014-12-11 06:38:04 +0200 (Thu, 11 Dec 2014) | 11 lines
  
  vdisk_blockio: Report invalid scatterlists
  
  It is possible for a target driver to pass a scatterlist via
  scst_cmd_set_tgt_sg() that is valid for the vdisk_fileio handler
  but not for the vdisk_blockio handler. Complain loudly if an invalid
  scatterlist is passed to vdisk_blockio because such scatterlists
  cause silent data corruption with most Linux block drivers.
  
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5934 | bvassche | 2014-12-11 14:31:03 +0200 (Thu, 11 Dec 2014) | 1 line
  
  scst_vdisk: Follow-up for r5932
........
  r5935 | bvassche | 2014-12-11 14:37:02 +0200 (Thu, 11 Dec 2014) | 1 line
  
  ib_srpt: Log P_Key during login
........
  r5936 | bvassche | 2014-12-12 11:29:42 +0200 (Fri, 12 Dec 2014) | 1 line
  
  scripts/generate-kernel-patch: Include scst_pg.sgml instead of sgv_cache.sgml
........
  r5937 | bvassche | 2014-12-12 11:34:55 +0200 (Fri, 12 Dec 2014) | 1 line
  
  doc/scst_pg.sgml: Remove trailing whitespace
........
  r5938 | bvassche | 2014-12-17 09:48:40 +0200 (Wed, 17 Dec 2014) | 1 line
  
  nightly build: Update kernel versions
........
  r5939 | vlnb | 2014-12-19 05:50:58 +0200 (Fri, 19 Dec 2014) | 3 lines
  
  Fallback to the old qla driver if the git one not detected
........
  r5940 | vlnb | 2014-12-19 05:55:14 +0200 (Fri, 19 Dec 2014) | 7 lines
  
  Replace in cases, where sporadic failures are possible, HARDWARE ERROR
  by INTERNAL TARGET FAILURE, which is retriable (some OS'es don't retry
  HARDWARE ERROR)
  
  Reported and suggested by Shahar Salzman <shahar.salzman@kaminario.com>
........
  r5941 | vlnb | 2014-12-20 05:48:07 +0200 (Sat, 20 Dec 2014) | 7 lines
  
  scst_vdisk: Only accept NAA IDs allowed by SPC
  
  See also paragraph 7.8.6.6 NAA designator format in SPC-4.
  
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5942 | vlnb | 2014-12-20 05:49:23 +0200 (Sat, 20 Dec 2014) | 11 lines
  
  scst_vdisk: Remove superfluous llseek() calls
  
  vfs_read() and vfs_write() ignore the file offset set by llseek().
  Hence remove the llseek() calls that occur just before vfs_read() and
  vfs_write(). See also the implementation in the Linux kernel of the
  pread64() and pwrite64() system calls for examples of code that uses
  vfs_read() and vfs_write().
  
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5943 | bvassche | 2014-12-22 14:28:13 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code spelling fix: Equivilant -> Equivalent
........
  r5944 | bvassche | 2014-12-22 14:28:56 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code spelling fix: accesss -> access
........
  r5945 | bvassche | 2014-12-22 14:29:51 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code spelling fix: addres -> address
........
  r5946 | bvassche | 2014-12-22 14:31:08 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code spelling fix: authentification -> authentication
........
  r5947 | bvassche | 2014-12-22 14:32:30 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code comment spelling fix: explicitely -> explicitly
........
  r5948 | bvassche | 2014-12-22 14:33:06 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code comment spelling fix: hander -> handler
........
  r5949 | bvassche | 2014-12-22 14:33:37 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code comment spelling fix: loosing -> losing
........
  r5950 | bvassche | 2014-12-22 14:35:00 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Spelling fix: occured -> occurred
........
  r5951 | bvassche | 2014-12-22 14:35:51 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Source code comment spelling fix: refering -> referring
........
  r5952 | bvassche | 2014-12-22 14:36:47 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Spelling fix: shrinked -> shrunk
........
  r5953 | bvassche | 2014-12-22 15:08:34 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Spelling fix: choosen -> chosen
........
  r5954 | bvassche | 2014-12-22 15:09:20 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Spelling fix: existant -> existent
........
  r5955 | bvassche | 2014-12-22 15:10:41 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Update for kernel 3.18
........
  r5956 | bvassche | 2014-12-22 15:15:55 +0200 (Mon, 22 Dec 2014) | 1 line
  
  Spelling fix: immediatelly -> immediately
........
  r5957 | bvassche | 2014-12-24 16:28:36 +0200 (Wed, 24 Dec 2014) | 1 line
  
  nightly build: Add kernel 3.18
........
  r5958 | bvassche | 2014-12-29 14:14:52 +0200 (Mon, 29 Dec 2014) | 1 line
  
  scst_lib: Convert spaces into tabs (reported by checkpatch)
........
  r5959 | bvassche | 2015-01-06 15:25:28 +0200 (Tue, 06 Jan 2015) | 1 line
  
  scst_calc_block_shift: Log block shift and sector size upon mismatch
........
  r5960 | bvassche | 2015-01-07 11:20:06 +0200 (Wed, 07 Jan 2015) | 4 lines
  
  scst_local: Fix unique per session sas address
  
  Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>
........
  r5961 | bvassche | 2015-01-09 14:23:25 +0200 (Fri, 09 Jan 2015) | 4 lines
  
  scst_sysfs: return EINVAL on too big LUN
  
  Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>
........
  r5962 | bvassche | 2015-01-10 17:52:57 +0200 (Sat, 10 Jan 2015) | 1 line
  
  nightly build: Update kernel versions
........
  r5963 | bvassche | 2015-01-13 10:42:28 +0200 (Tue, 13 Jan 2015) | 10 lines
  
  scst: Switch to thread context before executing a reservation command
  
  Persistent reservation commands need thread context because
  scst_pr_is_cmd_allowed() locks the PR mutex. Reservation commands
  either need BH or thread context. Hence switch from atomic to
  thread context before processing such commands.
  
  Reported-by: Shahar Salzman <shahar.salzman@kaminario.com>
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5964 | bvassche | 2015-01-13 10:51:08 +0200 (Tue, 13 Jan 2015) | 5 lines
  
  scst_parse_unmap_descriptors(): Avoid using GFP_KERNEL in atomic context
  
  Reported-by: Shahar Salzman <shahar.salzman@kaminario.com>
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5965 | bvassche | 2015-01-13 10:55:46 +0200 (Tue, 13 Jan 2015) | 68 lines
  
  qla2x00t: Copy entire SCST sense buffer to q2x ctio
  
  There seems to be a bug in passing sense information to QLA HBAs, where 
  the last 2 bytes of the sense data (ASC, ASCQ) are not copied to the low 
  level sense buffer.
  We encountered this in ESX, which relies on these 2 bytes to parse the 
  MISCOMPARE sense code (0xE1, 0x1D, 0x00).
  Bellow is a simple test to recreate this issue, but during vMotion 
  operations (where VMs are moved from one host to another), this may 
  cause the operation to fail leaving the VM in an inconsistent state.
  
  The test I ran to verify that we are indeed missing the bytes is the 
  following:
  1. Create a SCST based device
  2. Expose the device to 2 ESX hosts
  3. Format the device as VMFS5, create a test directory
  4. From both hosts, I start writing to this directory (no VMs involved, 
  just write normal files)
  
  At this stage, both ESX hosts try to take access to the directory.
  The VMFS filesystem contains a per-directory lock which is managed by 
  COMPARE AND WRITE command.
  Each ESX will attempt to change the VMFS lock location from unlocked to 
  locked to create the new file.
  
  Obviously there are bound to be failures (which are equivalent to 
  programming locking conflicts), these are reported by the MISCOMPARE 
  sense code.
  Upon these MISCOMPARE errors, the host will re-try taking the lock until 
  it succeeds, and will then proceed to perform the write operation on the 
  directory.
  
  Due to the bug in copying the sense buffer from the SCST core to the QLA 
  ctio, instead of the full sense code, only the key (0xE) is sent, and 
  ESX does not know how to handle it resulting in IO error.
  
  Here are the errors as they appear on the command line:
  /vmfs/volumes/54a297c4-ca5af1cc-7f94-002219d20f28/ats_test # 
  ./open_close_test-esx2.sh
  ./open_close_test-esx2.sh: line 8: can't create 
  ats_fileoptest-esx2_1.txt: Input/output error
  ./open_close_test-esx2.sh: line 8: can't create 
  ats_fileoptest-esx2_21.txt: Input/output error
  ./open_close_test-esx2.sh: line 8: can't create 
  ats_fileoptest-esx2_110.txt: Input/output error
  ./open_close_test-esx2.sh: line 8: can't create 
  ats_fileoptest-esx2_111.txt: Input/output error
  
  In the /var/log/vmkernel.log, we can see that the sense information is 
  missing (0xE, 0x0, 0x0) instead of (0xE, 0x1D, 0x0).
  2014-12-30T12:13:20.714Z cpu6:33519)ScsiDeviceIO: 2338: 
  Cmd(0x412e84f957c0) 0x89, CmdSN 0x234d from world 519051 to dev 
  "eui.0024f400d5020007" failed H:0x0 D:0x2 P:0x0 Valid sense data: 0xe 0x0 0x0.
  2014-12-30T12:13:20.766Z cpu6:33519)ScsiDeviceIO: 2338: 
  Cmd(0x412e84f91d00) 0x89, CmdSN 0x2350 from world 519051 to dev 
  "eui.0024f400d5020007" failed H:0x0 D:0x2 P:0x0 Valid sense data: 0xe 0x0 0x0.
  2014-12-30T12:13:20.766Z cpu6:33519)ScsiDeviceIO: 2338: 
  Cmd(0x412e80449fc0) 0x89, CmdSN 0x234f from world 519051 to dev 
  "eui.0024f400d5020007" failed H:0x0 D:0x2 P:0x0 Valid sense data: 0xe 0x0 0x0.
  
  This patch fixes this issue, the test will run without a problem with the
  fix (no IO errors, all the files are properly written to the directory).
  
  Signed-off-by: Shahar Salzman <shahar.salzman@kaminario.com>
  Reviewed-by: Eran Mann <eran.mann@kaminario.com>
  [bvanassche: simplified implementation]
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5966 | bvassche | 2015-01-13 11:38:09 +0200 (Tue, 13 Jan 2015) | 5 lines
  
  qla2x00t: Register for RSCNs in target mode
  
  The QLogic firmware and qla2xxx do not register for RSCNs in
  target-only mode, so do that explicitly.
........
  r5967 | bvassche | 2015-01-14 10:06:12 +0200 (Wed, 14 Jan 2015) | 1 line
  
  scst_targ: Use tabs instead of spaces for indentation (detected by checkpatch)
........
  r5968 | bvassche | 2015-01-15 10:58:39 +0200 (Thu, 15 Jan 2015) | 4 lines
  
  scst_targ: Avoid triggering a kernel panic if dev_user_parse() returns SCST_CMD_STATE_STOP
  
  Reported-by: Ilan Steinberg <ilan.steinberg@kaminario.com>
........
  r5969 | vlnb | 2015-01-16 03:21:10 +0200 (Fri, 16 Jan 2015) | 3 lines
  
  Fix READ BUFFER and WRITE BUFFER commands
........
  r5970 | vlnb | 2015-01-16 05:16:26 +0200 (Fri, 16 Jan 2015) | 3 lines
  
  Follow up for r5968
........
  r5971 | vlnb | 2015-01-16 05:53:29 +0200 (Fri, 16 Jan 2015) | 5 lines
  
  Report during user devices unjam LUN NOT SUPPORTED sense
  
  Reported-By: shahar.salzman <shahar.salzman@kaminario.com>
........
  r5972 | bvassche | 2015-01-16 15:01:58 +0200 (Fri, 16 Jan 2015) | 2 lines
  
  scst.spec.in: Rename variable kver into kversion
........
  r5973 | bvassche | 2015-01-16 15:12:22 +0200 (Fri, 16 Jan 2015) | 2 lines
  
  scst.spec.in: Pass kernel version via RPM-variable %{kversion} instead of shell variable ${KVER}
........
  r5974 | bvassche | 2015-01-16 15:16:06 +0200 (Fri, 16 Jan 2015) | 6 lines
  
  scst.spec.in: Determine version number correctly on a koji server
  
  This patch has been tested on a koji build server and also on four
  different RPM-based distributions (CentOS 7, Fedora 20, openSuSE 13.2
  and SLES 11 SP3).
........
  r5975 | bvassche | 2015-01-16 18:12:38 +0200 (Fri, 16 Jan 2015) | 1 line
  
  scst.spec.in: Leave out kernel version from RPM name
........
  r5976 | bvassche | 2015-01-16 18:20:10 +0200 (Fri, 16 Jan 2015) | 1 line
  
  scst.spec.in: Add DKMS support
........
  r5977 | vlnb | 2015-01-20 06:18:07 +0200 (Tue, 20 Jan 2015) | 3 lines
  
  Revert r5964 as not needed
........
  r5978 | vlnb | 2015-01-20 06:20:13 +0200 (Tue, 20 Jan 2015) | 3 lines
  
  Revert r5963 as not needed
........
  r5979 | bvassche | 2015-01-20 17:04:23 +0200 (Tue, 20 Jan 2015) | 13 lines
  
  scst: Rework SCSI pass-through support for kernel versions >= 2.6.30
  
  Changes in this patch:
  - Rework the SCSI pass-through code such that for kernel versions
    >= 2.6.30 the scst_exec_req_fifo patch is no longer needed.
  - Modify the pass-through code such that blk_rq_append_bio() is only
    called for kernel version 2.6.30. For later kernel versions
    blk_make_request() is called instead.
  - Rework scst_scsi_exec_async().
  - Add debug tracing of SCSI pass-through result status.
  - Add a lockdep_assert_held() call in scsi_end_async().
........
  r5980 | bvassche | 2015-01-20 19:13:13 +0200 (Tue, 20 Jan 2015) | 1 line
  
  nightly build: Update kernel versions
........
  r5981 | vlnb | 2015-01-21 06:15:42 +0200 (Wed, 21 Jan 2015) | 3 lines
  
  Follow up for r5979
........
  r5982 | vlnb | 2015-01-21 06:20:53 +0200 (Wed, 21 Jan 2015) | 5 lines
  
  Fix returning changeable values for caching mode page
  
  Reported by Consus <consus@gmx.com>
........
  r5983 | bvassche | 2015-01-21 15:11:56 +0200 (Wed, 21 Jan 2015) | 1 line
  
  scst.h: Fix a sparse warning for kernels 2.6.29..2.6.31
........
  r5984 | vlnb | 2015-01-22 07:03:17 +0200 (Thu, 22 Jan 2015) | 9 lines
  
  [PATCH] scst_local: Fix bidirectional command support
  
  scsi_setup_cmnd() sets sc_data_direction to DMA_TO_DEVICE for bidirectional
  commands. Hence test SCpnt->request->next_rq instead of sc_data_direction
  to figure out whether or not a command is bidirectional.
  
  Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
........
  r5985 | vlnb | 2015-01-22 07:06:45 +0200 (Thu, 22 Jan 2015) | 12 lines
  
  [PATCH] scst_main: Suppress a checkpatch warning triggered by INIT_CACHEP{,_ALIGN}
  
  Avoid that checkpatch v3.18 reports the following warning for these
  two macros:
  
  WARNING: Macros with flow control statements should be avoided
  
  This patch does not change any functionality.
  
  Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
........
  r5986 | vlnb | 2015-01-22 07:09:17 +0200 (Thu, 22 Jan 2015) | 9 lines
  
  scst_vdisk: Micro-optimize vdisk_caching_pg
  
  This patch does not change any behavior but micro-optimizes
  vdisk_caching_pg(). Declaring the array caching_pg[] const reduces
  11 bytes from the assembler code of this function.
  
  Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
........
  r5987 | vlnb | 2015-01-22 07:10:42 +0200 (Thu, 22 Jan 2015) | 10 lines
  
  scst: Suppress a smatch warning in vdisk_unmap_range()
  
  Avoid that the static source code analysis tool 'smatch' reports
  the following warning:
  
  vdisk_unmap_range() warn: should 'blocks << cmd->dev->block_shift' be a 64 bit type?
  
  Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
........
  r5988 | vlnb | 2015-01-22 07:13:59 +0200 (Thu, 22 Jan 2015) | 27 lines
  
  scst_vdisk: Fix zero-copy read for tmpfs
  
  For some filesystems, e.g. tmpfs, address_space.readpage is NULL.
  Disable zero-copy reading for such filesystems. See also shmem_aops
  in mm/shmem.c. See also inode_init_always() and empty_aops in fs/inode.c.
  
  This patch avoids that the following call trace is triggered:
  
  BUG: unable to handle kernel NULL pointer dereference at (null)
  Call Trace:
   [<ffffffffa0547d66>] prepare_read+0x106/0x1d0 [scst_vdisk]
   [<ffffffffa0547f20>] fileio_alloc_data_buf+0xf0/0x330 [scst_vdisk]
   [<ffffffffa046fc9b>] scst_prepare_space+0x9b/0x6e0 [scst]
   [<ffffffffa047d4d5>] scst_process_active_cmd+0x545/0x840 [scst]
   [<ffffffffa047dad2>] scst_cmd_init_done+0x302/0x5d0 [scst]
   [<ffffffffa0563ab2>] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst]
   [<ffffffffa056a9ea>] scsi_cmnd_start+0x25a/0x550 [iscsi_scst]
   [<ffffffffa056b4a8>] cmnd_rx_start+0x148/0x1a0 [iscsi_scst]
   [<ffffffffa056e4f8>] process_read_io+0x3b8/0x800 [iscsi_scst]
   [<ffffffffa056ea07>] scst_do_job_rd+0xc7/0x220 [iscsi_scst]
   [<ffffffffa056efed>] istrd+0x16d/0x2e0 [iscsi_scst]
   [<ffffffff81079efd>] kthread+0xed/0x110
   [<ffffffff817227fc>] ret_from_fork+0x7c/0xb0
  
  Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
........
  r5989 | vlnb | 2015-01-24 07:37:57 +0200 (Sat, 24 Jan 2015) | 5 lines
  
  scst_local: Rework data direction detection code
  
  Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
........
  r5990 | bvassche | 2015-01-26 13:32:32 +0200 (Mon, 26 Jan 2015) | 1 line
  
  ib_srpt: Detect Mellanox OFED 2.3 correctly
........
  r5991 | vlnb | 2015-01-28 07:07:46 +0200 (Wed, 28 Jan 2015) | 3 lines
  
  Cleanups
........


git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5993 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Yan Burman
2015-01-28 12:12:32 +00:00
parent e6e1752b8f
commit bb7f5caef2
114 changed files with 4515 additions and 18152 deletions
+17 -10
View File
@@ -141,7 +141,7 @@ help:
all:
cd $(SCST_DIR) && $(MAKE) $@
# @if [ -d $(DOC_DIR) ]; then cd $(DOC_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; else if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi fi
# @if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi
# @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
# @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi
@@ -152,7 +152,7 @@ all:
install:
cd $(SCST_DIR) && $(MAKE) $@
# @if [ -d $(DOC_DIR) ]; then cd $(DOC_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; else if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi fi
# @if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi
# @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
# @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi
@@ -163,7 +163,7 @@ install:
uninstall:
cd $(SCST_DIR) && $(MAKE) $@
# @if [ -d $(DOC_DIR) ]; then cd $(DOC_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; else if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi fi
# @if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi
# @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
@if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi
@@ -228,10 +228,10 @@ docs_extraclean:
scstadm:
cd $(SCSTADM_DIR) && $(MAKE) all
scstadm_install:
scstadm_install:
cd $(SCSTADM_DIR) && $(MAKE) install
scstadm_uninstall:
scstadm_uninstall:
cd $(SCSTADM_DIR) && $(MAKE) uninstall
scstadm_clean:
@@ -252,7 +252,7 @@ qla_install:
qla_uninstall:
cd $(QLA_DIR) && $(MAKE) uninstall
qla_clean:
qla_clean:
cd $(QLA_INI_DIR) && $(MAKE) clean
cd $(QLA_DIR) && $(MAKE) clean
@@ -284,7 +284,7 @@ iscsi_install:
iscsi_uninstall:
cd $(ISCSI_DIR) && $(MAKE) uninstall
iscsi_clean:
iscsi_clean:
cd $(ISCSI_DIR) && $(MAKE) clean
iscsi_extraclean:
@@ -383,7 +383,13 @@ fcst_extraclean:
scst-dist-gzip:
name=scst && \
mkdir $${name}-$(VERSION) && \
{ scripts/list-source-files | \
{ if [ -h qla2x00t ] || { mount | grep "on $$PWD/qla2x00t type"; }; \
then \
scripts/list-source-files | grep -v ^qla2x00t/; \
find qla2x00t/ -type f; \
else \
scripts/list-source-files; \
fi | \
grep -E '^doc/|^fcst/|^iscsi-scst/|^Makefile|^qla2x00t/|^scst.spec|^scst/|^scst_local/|^srpt/'|\
tar -T- -cf- | \
tar -C $${name}-$(VERSION) -xf-; } && \
@@ -402,8 +408,9 @@ scst-rpm:
cp $${name}-$(VERSION).tar.bz2 $${rpmtopdir}/SOURCES && \
sed "s/@rpm_version@/$(VERSION)/g" \
<$${name}.spec.in >$${name}.spec; \
MAKE="$(MAKE)" \
rpmbuild --define="%_topdir $${rpmtopdir}" -ba $${name}.spec && \
MAKE="$(MAKE)" rpmbuild --define="%_topdir $${rpmtopdir}" \
$(if $(KVER),--define="%kversion $(KVER)") \
-ba $${name}.spec && \
rm -f $${name}-$(VERSION).tar.bz2
rpm:
+1 -1
View File
@@ -8,7 +8,7 @@ RTFS = $(SRCS:.sgml=.rtf)
COMMAND=linuxdoc --backend=
all: txt pdf html
all: pdf html
txt: $(TXTS)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 42 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

+1674 -1795
View File
File diff suppressed because it is too large Load Diff
-335
View File
@@ -1,335 +0,0 @@
<!doctype linuxdoc system>
<article>
<title>
SCST SGV cache description
</title>
<author>
<name>Vladislav Bolkhovitin</name>
</author>
<date>Version 2.1.0</date>
<toc>
<sect>Introduction
<p>
SCST SGV cache is a memory management subsystem in SCST. One can call it
a "memory pool", but Linux kernel already have a mempool interface,
which serves different purposes. SGV cache provides to SCST core, target
drivers and backend dev handlers facilities to allocate, build and cache
SG vectors for data buffers. The main advantage of it is the caching
facility, when it doesn't free to the system each vector, which is not
used anymore, but keeps it for a while (possibly indefinitely) to let it
be reused by the next consecutive command. This allows to:
<itemize>
<item> Reduce commands processing latencies and, hence, improve performance;
<item> Make commands processing latencies predictable, which is essential
for RT applications.
</itemize>
The freed SG vectors are kept by the SGV cache either for some (possibly
indefinite) time, or, optionally, until the system needs more memory and
asks to free some using the set_shrinker() interface. Also the SGV cache
allows to:
<itemize>
<item> Cluster pages together. "Cluster" means merging adjacent pages in a
single SG entry. It allows to have less SG entries in the resulting SG
vector, hence improve performance handling it as well as allow to
work with bigger buffers on hardware with limited SG capabilities.
<item> Set custom page allocator functions. For instance, scst_user device
handler uses this facility to eliminate unneeded mapping/unmapping of
user space pages and avoid unneeded IOCTL calls for buffers allocations.
In fileio_tgt application, which uses a regular malloc() function to
allocate data buffers, this facility allows ~30% less CPU load and
considerable performance increase.
<item> Prevent each initiator or all initiators altogether to allocate too
much memory and DoS the target. Consider 10 initiators, which can have
access to 10 devices each. Any of them can queue up to 64 commands, each
can transfer up to 1MB of data. So, all of them in a peak can allocate
up to 10*10*64 = ~6.5GB of memory for data buffers. This amount must be
limited somehow and the SGV cache performs this function.
</itemize>
<sect> Implementation
<p>
From implementation POV the SGV cache is a simple extension of the kmem
cache. It can work in 2 modes:
<enum>
<item> With fixed size buffers.
<item> With a set of power 2 size buffers. In this mode each SGV cache
(struct sgv_pool) has SGV_POOL_ELEMENTS (11 currently) of kmem caches.
Each of those kmem caches keeps SGV cache objects (struct sgv_pool_obj)
corresponding to SG vectors with size of order X pages. For instance,
request to allocate 4 pages will be served from kmem cache&lsqb;2&rsqb, since the
order of the of number of requested pages is 2. If later request to
allocate 11KB comes, the same SG vector with 4 pages will be reused (see
below). This mode is in average allows less memory overhead comparing
with the fixed size buffers mode.
</enum>
Consider how the SGV cache works in the set of buffers mode. When a
request to allocate new SG vector comes, sgv_pool_alloc() via
sgv_get_obj() checks if there is already a cached vector with that
order. If yes, then that vector will be reused and its length, if
necessary, will be modified to match the requested size. In the above
example request for 11KB buffer, 4 pages vector will be reused and
modified using trans_tbl to contain 3 pages and the last entry will be
modified to contain the requested length - 2*PAGE_SIZE. If there is no
cached object, then a new sgv_pool_obj will be allocated from the
corresponding kmem cache, chosen by the order of number of requested
pages. Then that vector will be filled by pages and returned.
In the fixed size buffers mode the SGV cache works similarly, except
that it always allocate buffer with the predefined fixed size. I.e.
even for 4K request the whole buffer with predefined size, say, 1MB,
will be used.
In both modes, if size of a request exceeds the maximum allowed for
caching buffer size, the requested buffer will be allocated, but not
cached.
Freed cached sgv_pool_obj objects are actually freed to the system
either by the purge work, which is scheduled once in 60 seconds, or in
sgv_shrink() called by system, when it's asking for memory.
<sect> Interface
<sect1> sgv_pool *sgv_pool_create()
<p>
<verb>
struct sgv_pool *sgv_pool_create(
const char *name,
enum sgv_clustering_types clustered, int single_alloc_pages,
bool shared, int purge_interval)
</verb>
This function creates and initializes an SGV cache. It has the following
arguments:
<itemize>
<item> <bf/name/ - the name of the SGV cache
<item> <bf/clustered/ - sets type of the pages clustering. The type can be:
<itemize>
<item> <bf/sgv_no_clustering/ - no clustering performed.
<item> <bf/sgv_tail_clustering/ - a page will only be merged with the latest
previously allocated page, so the order of pages in the SG will be
preserved
<item> <bf/sgv_full_clustering/ - free merging of pages at any place in
the SG is allowed. This mode usually provides the best merging
rate.
</itemize>
<item> <bf/single_alloc_pages/ - if 0, then the SGV cache will work in the set of
power 2 size buffers mode. If >0, then the SGV cache will work in the
fixed size buffers mode. In this case single_alloc_pages sets the
size of each buffer in pages.
<item> <bf/shared/ - sets if the SGV cache can be shared between devices or not.
The cache sharing allowed only between devices created inside the same
address space. If an SGV cache is shared, each subsequent call of
sgv_pool_create() with the same cache name will not create a new cache,
but instead return a reference to it.
<item> <bf/purge_interval/ - sets the cache purging interval. I.e. an SG buffer
will be freed if it's unused for time t purge_interval <= t <
2*purge_interval. If purge_interval is 0, then the default interval
will be used (60 seconds). If purge_interval <0, then the automatic
purging will be disabled. Shrinking by the system's demand will also
be disabled.
</itemize>
Returns the resulting SGV cache or NULL in case of any error.
<sect1> void sgv_pool_del()
<p>
<verb>
void sgv_pool_del(
struct sgv_pool *pool)
</verb>
This function deletes the corresponding SGV cache. If the cache is
shared, it will decrease its reference counter. If the reference counter
reaches 0, the cache will be destroyed.
<sect1> void sgv_pool_flush()
<p>
<verb>
void sgv_pool_flush(
struct sgv_pool *pool)
</verb>
This function flushes, i.e. frees, all the cached entries in the SGV
cache.
<sect1> void sgv_pool_set_allocator()
<p>
<verb>
void sgv_pool_set_allocator(
struct sgv_pool *pool,
struct page *(*alloc_pages_fn)(struct scatterlist *sg, gfp_t gfp, void *priv),
void (*free_pages_fn)(struct scatterlist *sg, int sg_count, void *priv));
</verb>
This function allows to set for the SGV cache a custom pages allocator. For
instance, scst_user uses such function to supply to the cache mapped from
user space pages.
<bf/alloc_pages_fn()/ has the following parameters:
<itemize>
<item> <bf/sg/ - SG entry, to which the allocated page should be added.
<item> <bf/gfp/ - the allocation GFP flags
<item> <bf/priv/ - pointer to a private data supplied to sgv_pool_alloc()
</itemize>
This function should return the allocated page or NULL, if no page was
allocated.
<bf/free_pages_fn()/ has the following parameters:
<itemize>
<item> <bf/sg/ - SG vector to free
<item> <bf/sg_count/ - number of SG entries in the sg
<item> <bf/priv/ - pointer to a private data supplied to the
corresponding sgv_pool_alloc()
</itemize>
<sect1> struct scatterlist *sgv_pool_alloc()
<p>
<verb>
struct scatterlist *sgv_pool_alloc(
struct sgv_pool *pool,
unsigned int size,
gfp_t gfp_mask,
int flags,
int *count,
struct sgv_pool_obj **sgv,
struct scst_mem_lim *mem_lim,
void *priv)
</verb>
This function allocates an SG vector from the SGV cache. It has the
following parameters:
<itemize>
<item> <bf/pool/ - the cache to alloc from
<item> <bf/size/ - size of the resulting SG vector in bytes
<item> <bf/gfp_mask/ - the allocation mask
<item> <bf/flags/ - the allocation flags. The following flags are possible and
can be set using OR operation:
<enum>
<item> <bf/SGV_POOL_ALLOC_NO_CACHED/ - the SG vector must not be cached.
<item> <bf/SGV_POOL_NO_ALLOC_ON_CACHE_MISS/ - don't do an allocation on a
cache miss.
<item> <bf/SGV_POOL_RETURN_OBJ_ON_ALLOC_FAIL/ - return an empty SGV object,
i.e. without the SG vector, if the allocation can't be completed.
For instance, because SGV_POOL_NO_ALLOC_ON_CACHE_MISS flag set.
</enum>
<item> <bf/count/ - the resulting count of SG entries in the resulting SG vector.
<item> <bf/sgv/ - the resulting SGV object. It should be used to free the
resulting SG vector.
<item> <bf/mem_lim/ - memory limits, see below.
<item> <bf/priv/ - pointer to private for this allocation data. This pointer will
be supplied to alloc_pages_fn() and free_pages_fn() and can be
retrieved by sgv_get_priv().
</itemize>
This function returns pointer to the resulting SG vector or NULL in case
of any error.
<sect1> void sgv_pool_free()
<p>
<verb>
void sgv_pool_free(
struct sgv_pool_obj *sgv,
struct scst_mem_lim *mem_lim)
</verb>
This function frees previously allocated SG vector, referenced by SGV
cache object sgv.
<sect1> void *sgv_get_priv(struct sgv_pool_obj *sgv)
<p>
<verb>
void *sgv_get_priv(
struct sgv_pool_obj *sgv)
</verb>
This function allows to get the allocation private data for this SGV
cache object sgv. The private data are set by sgv_pool_alloc().
<sect1> void scst_init_mem_lim()
<p>
<verb>
void scst_init_mem_lim(
struct scst_mem_lim *mem_lim)
</verb>
This function initializes memory limits structure mem_lim according to
the current system configuration. This structure should be latter used
to track and limit allocated by one or more SGV caches memory.
<sect> Runtime information and statistics.
<p>
Runtime information and statistics is available in /sys/kernel/scst_tgt/sgv.
</article>
+3 -1
View File
@@ -181,7 +181,9 @@ static u32 ft_sess_hash(u32 port_id)
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 41)) && \
! (LINUX_VERSION_CODE >> 8 == KERNEL_VERSION(3, 2, 0) >> 8 && \
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 44)) && \
!defined(CONFIG_SUSE_KERNEL)
!defined(CONFIG_SUSE_KERNEL) && \
(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 || \
(RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 6))
/*
* See also commit 4b20db3 (kref: Implement kref_get_unless_zero v3 -- v3.8).
* See also commit e3a5505 in branch stable/linux-3.4.y (v3.4.41).
+1 -1
View File
@@ -206,7 +206,7 @@ config SCSI_MULTI_LUN
mobile phone in mass storage mode. This option forces the kernel to
probe for all LUNs by default. This setting can be overriden by
max_luns boot/module parameter. Note that this option does not affect
devices conforming to SCSI-3 or higher as they can explicitely report
devices conforming to SCSI-3 or higher as they can explicitly report
their number of LUNs. It is safe to say Y here unless you have one of
those rare devices which reacts in an unexpected way when probed for
multiple LUNs.
+1 -1
View File
@@ -206,7 +206,7 @@ config SCSI_MULTI_LUN
mobile phone in mass storage mode. This option forces the kernel to
probe for all LUNs by default. This setting can be overriden by
max_luns boot/module parameter. Note that this option does not affect
devices conforming to SCSI-3 or higher as they can explicitely report
devices conforming to SCSI-3 or higher as they can explicitly report
their number of LUNs. It is safe to say Y here unless you have one of
those rare devices which reacts in an unexpected way when probed for
multiple LUNs.
+1 -1
View File
@@ -206,7 +206,7 @@ config SCSI_MULTI_LUN
mobile phone in mass storage mode. This option forces the kernel to
probe for all LUNs by default. This setting can be overriden by
max_luns boot/module parameter. Note that this option does not affect
devices conforming to SCSI-3 or higher as they can explicitely report
devices conforming to SCSI-3 or higher as they can explicitly report
their number of LUNs. It is safe to say Y here unless you have one of
those rare devices which reacts in an unexpected way when probed for
multiple LUNs.
+1 -1
View File
@@ -413,7 +413,7 @@ echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
Below is an advanced sample script, which configures more virtual
devices of various types, including virtual CDROM and 2 targets, one
with all default parameters, another one with some not default
parameters, incoming and outgoing user names for CHAP authentification,
parameters, incoming and outgoing user names for CHAP authentication,
and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520,
which will see another set of devices. Also this sample configures CHAP
authentication for discovery sessions and iSNS server with access
+1 -1
View File
@@ -242,7 +242,7 @@ echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
Below is an advanced sample script, which configures more virtual
devices of various types, including virtual CDROM and 2 targets, one
with all default parameters, another one with some not default
parameters, incoming and outgoing user names for CHAP authentification,
parameters, incoming and outgoing user names for CHAP authentication,
and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520,
which will see another set of devices. Also this sample configures CHAP
authentication for discovery sessions and iSNS server with access
-1
View File
@@ -40,7 +40,6 @@ work.
cd /usr/src/linux-2.6.39-gentoo-r3
patch -p1 < /root/scst/iscsi-scst/kernel/patches/put_page_callback-2.6.39.patch
patch -p1 < /root/scst/scst/kernel/scst_exec_req_fifo-2.6.39.patch
make clean
-1
View File
@@ -21,7 +21,6 @@ the example below):
cd /usr/src/kernels/linux-2.6.38.8
patch -p1 < $HOME/scst/iscsi-scst/kernel/patches/put_page_callback-2.6.38.patch
patch -p1 < $HOME/scst/scst/kernel/scst_exec_req_fifo-2.6.38.patch
make clean
Next, build and install the kernel:
+2 -2
View File
@@ -339,7 +339,7 @@ static int add_conn(void __user *ptr)
session = session_lookup(target, info.sid);
if (!session) {
PRINT_ERROR("Session %lld not found",
(long long unsigned int)info.tid);
(unsigned long long int)info.tid);
err = -ENOENT;
goto out_unlock;
}
@@ -383,7 +383,7 @@ static int del_conn(void __user *ptr)
session = session_lookup(target, info.sid);
if (!session) {
PRINT_ERROR("Session %llx not found",
(long long unsigned int)info.sid);
(unsigned long long int)info.sid);
err = -ENOENT;
goto out_unlock;
}
+5 -5
View File
@@ -533,7 +533,7 @@ static void conn_rsp_timer_fn(unsigned long arg)
"%s (SID %llx), closing connection",
iscsi_get_timeout(cmnd)/HZ,
conn->session->initiator_name,
(long long unsigned int)
(unsigned long long int)
conn->session->sid);
/*
* We must call mark_conn_closed() outside of
@@ -764,13 +764,13 @@ static int conn_setup_sock(struct iscsi_conn *conn)
mm_segment_t oldfs;
struct iscsi_session *session = conn->session;
TRACE_DBG("%llx", (long long unsigned int)session->sid);
TRACE_DBG("%llx", (unsigned long long int)session->sid);
conn->sock = SOCKET_I(conn->file->f_dentry->d_inode);
if (conn->sock->ops->sendpage == NULL) {
PRINT_ERROR("Socket for sid %llx doesn't support sendpage()",
(long long unsigned int)session->sid);
(unsigned long long int)session->sid);
res = -EINVAL;
goto out;
}
@@ -809,7 +809,7 @@ void conn_free(struct iscsi_conn *conn)
TRACE_ENTRY();
TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn,
session, (long long unsigned int)session->sid, conn->cid);
session, (unsigned long long int)session->sid, conn->cid);
lockdep_assert_held(&conn->target->target_mutex);
@@ -925,7 +925,7 @@ int iscsi_conn_alloc(struct iscsi_session *session,
}
TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn,
(long long unsigned int)session->sid, info->cid);
(unsigned long long int)session->sid, info->cid);
conn->transport = t;
+2 -2
View File
@@ -2374,8 +2374,8 @@ static int cmnd_abort_pre_checks(struct iscsi_cmnd *req, int *status)
if (req_hdr->lun != hdr->lun) {
PRINT_ERROR("ABORT TASK: LUN mismatch: req LUN "
"%llx, cmd LUN %llx, rtt %u",
(long long unsigned)be64_to_cpu(req_hdr->lun),
(long long unsigned)be64_to_cpu(hdr->lun),
(unsigned long long)be64_to_cpu(req_hdr->lun),
(unsigned long long)be64_to_cpu(hdr->lun),
req_hdr->rtt);
*status = ISCSI_RESPONSE_FUNCTION_REJECTED;
goto out_put;
+1 -1
View File
@@ -426,7 +426,7 @@ struct iscsi_cmnd {
/*
* Used only to abort not yet sent responses. Usage in
* cmnd_done() is only a side effect to have a lockless
* accesss to this list from always only a single thread
* access to this list from always only a single thread
* at any time. So, all responses live in the parent
* until it has the last reference put.
*/
+3 -3
View File
@@ -1336,7 +1336,7 @@ retry:
count, &off);
set_fs(oldfs);
TRACE_WRITE("sid %#Lx, cid %u, res %d, iov_len %zd",
(long long unsigned int)conn->session->sid,
(unsigned long long int)conn->session->sid,
conn->cid, res, iop->iov_len);
if (unlikely(res <= 0)) {
if (res == -EAGAIN) {
@@ -1467,7 +1467,7 @@ retry2:
"index %lu, offset %u, size %u, cmd %p, "
"page %p)", (sendpage != sock_no_sendpage) ?
"sendpage" : "sock_no_sendpage",
(long long unsigned int)conn->session->sid,
(unsigned long long int)conn->session->sid,
conn->cid, res, page->index,
offset, size, write_cmnd, page);
if (unlikely(res <= 0)) {
@@ -1553,7 +1553,7 @@ out_err:
{
#endif
PRINT_ERROR("error %d at sid:cid %#Lx:%u, cmnd %p", res,
(long long unsigned int)conn->session->sid,
(unsigned long long int)conn->session->sid,
conn->cid, conn->write_cmnd);
}
if (ref_cmd_to_parent &&
@@ -0,0 +1,364 @@
=== modified file 'drivers/block/drbd/drbd_receiver.c'
--- old/drivers/block/drbd/drbd_receiver.c 2014-11-21 03:17:49 +0000
+++ new/drivers/block/drbd/drbd_receiver.c 2014-11-21 03:51:00 +0000
@@ -132,7 +132,7 @@ static int page_chain_free(struct page *
struct page *tmp;
int i = 0;
page_chain_for_each_safe(page, tmp) {
- put_page(page);
+ net_put_page(page);
++i;
}
return i;
=== modified file 'include/linux/mm_types.h'
--- old/include/linux/mm_types.h 2014-11-21 03:17:49 +0000
+++ new/include/linux/mm_types.h 2014-11-21 03:51:00 +0000
@@ -196,6 +196,17 @@ struct page {
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
int _last_cpupid;
#endif
+
+#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completion. It might look as not good to have this field here and
+ * it's better to have it in struct sk_buff, but it would make the code
+ * much more complicated and fragile, since all skb then would have to
+ * contain only pages with the same value in this field.
+ */
+ void *net_priv;
+#endif
}
/*
* The struct page can be forced to be double word aligned so that atomic ops
=== modified file 'include/linux/net.h'
--- old/include/linux/net.h 2014-11-21 03:17:49 +0000
+++ new/include/linux/net.h 2014-11-21 03:51:00 +0000
@@ -19,6 +19,7 @@
#define _LINUX_NET_H
#include <linux/stringify.h>
+#include <linux/mm.h>
#include <linux/random.h>
#include <linux/wait.h>
#include <linux/fcntl.h> /* For O_CLOEXEC and O_NONBLOCK */
@@ -285,6 +286,45 @@ int kernel_sendpage(struct socket *sock,
int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how);
+#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+/* Support for notification on zero-copy TCP transfer completion */
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+/*
+ * See comment for net_set_get_put_page_callbacks() why those functions
+ * don't need any protection.
+ */
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+#else
+static inline void net_get_page(struct page *page)
+{
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ put_page(page);
+}
+#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */
+
#define MODULE_ALIAS_NETPROTO(proto) \
MODULE_ALIAS("net-pf-" __stringify(proto))
=== modified file 'include/linux/skbuff.h'
--- old/include/linux/skbuff.h 2014-11-21 03:17:49 +0000
+++ new/include/linux/skbuff.h 2014-11-21 03:51:00 +0000
@@ -2145,7 +2145,7 @@ static inline struct page *skb_frag_page
*/
static inline void __skb_frag_ref(skb_frag_t *frag)
{
- get_page(skb_frag_page(frag));
+ net_get_page(skb_frag_page(frag));
}
/**
@@ -2168,7 +2168,7 @@ static inline void skb_frag_ref(struct s
*/
static inline void __skb_frag_unref(skb_frag_t *frag)
{
- put_page(skb_frag_page(frag));
+ net_put_page(skb_frag_page(frag));
}
/**
=== modified file 'net/Kconfig'
--- old/net/Kconfig 2014-11-21 03:17:49 +0000
+++ new/net/Kconfig 2014-11-21 03:51:00 +0000
@@ -75,6 +75,18 @@ config INET
Short answer: say Y.
+config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
+ bool "TCP/IP zero-copy transfer completion notification"
+ depends on INET
+ default SCST_ISCSI
+ ---help---
+ Adds support for sending a notification upon completion of a
+ zero-copy TCP/IP transfer. This can speed up certain TCP/IP
+ software. Currently this is only used by the iSCSI target driver
+ iSCSI-SCST.
+
+ If unsure, say N.
+
if INET
source "net/ipv4/Kconfig"
source "net/ipv6/Kconfig"
=== modified file 'net/ceph/pagevec.c'
--- old/net/ceph/pagevec.c 2014-11-21 03:17:49 +0000
+++ new/net/ceph/pagevec.c 2014-11-21 03:51:00 +0000
@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page **
for (i = 0; i < num_pages; i++) {
if (dirty)
set_page_dirty_lock(pages[i]);
- put_page(pages[i]);
+ net_put_page(pages[i]);
}
if (is_vmalloc_addr(pages))
vfree(pages);
=== modified file 'net/core/skbuff.c'
--- old/net/core/skbuff.c 2014-11-21 03:17:49 +0000
+++ new/net/core/skbuff.c 2014-11-21 03:51:00 +0000
@@ -426,7 +426,7 @@ struct sk_buff *__netdev_alloc_skb(struc
if (likely(data)) {
skb = build_skb(data, fragsz);
if (unlikely(!skb))
- put_page(virt_to_head_page(data));
+ net_put_page(virt_to_head_page(data));
}
} else {
skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask,
@@ -484,7 +484,7 @@ static void skb_clone_fraglist(struct sk
static void skb_free_head(struct sk_buff *skb)
{
if (skb->head_frag)
- put_page(virt_to_head_page(skb->head));
+ net_put_page(virt_to_head_page(skb->head));
else
kfree(skb->head);
}
@@ -808,7 +808,7 @@ int skb_copy_ubufs(struct sk_buff *skb,
if (!page) {
while (head) {
struct page *next = (struct page *)page_private(head);
- put_page(head);
+ net_put_page(head);
head = next;
}
return -ENOMEM;
@@ -1655,7 +1655,7 @@ EXPORT_SYMBOL(skb_copy_bits);
*/
static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
{
- put_page(spd->pages[i]);
+ net_put_page(spd->pages[i]);
}
static struct page *linear_to_page(struct page *page, unsigned int *len,
@@ -1708,7 +1708,7 @@ static bool spd_fill_page(struct splice_
spd->partial[spd->nr_pages - 1].len += *len;
return false;
}
- get_page(page);
+ net_get_page(page);
spd->pages[spd->nr_pages] = page;
spd->partial[spd->nr_pages].len = *len;
spd->partial[spd->nr_pages].offset = offset;
@@ -2167,7 +2167,7 @@ skb_zerocopy(struct sk_buff *to, struct
page = virt_to_head_page(from->head);
offset = from->data - (unsigned char *)page_address(page);
__skb_fill_page_desc(to, 0, page, offset, plen);
- get_page(page);
+ net_get_page(page);
j = 1;
len -= plen;
}
@@ -2821,7 +2821,7 @@ int skb_append_datato_frags(struct sock
copy);
frg_cnt++;
pfrag->offset += copy;
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
skb->truesize += copy;
atomic_add(copy, &sk->sk_wmem_alloc);
=== modified file 'net/core/sock.c'
--- old/net/core/sock.c 2014-11-21 03:17:49 +0000
+++ new/net/core/sock.c 2014-11-21 03:51:00 +0000
@@ -1881,7 +1881,7 @@ bool skb_page_frag_refill(unsigned int s
}
if (pfrag->offset + sz <= pfrag->size)
return true;
- put_page(pfrag->page);
+ net_put_page(pfrag->page);
}
pfrag->offset = 0;
@@ -2645,7 +2645,7 @@ void sk_common_release(struct sock *sk)
sk_refcnt_debug_release(sk);
if (sk->sk_frag.page) {
- put_page(sk->sk_frag.page);
+ net_put_page(sk->sk_frag.page);
sk->sk_frag.page = NULL;
}
=== modified file 'net/ipv4/Makefile'
--- old/net/ipv4/Makefile 2014-11-21 03:17:49 +0000
+++ new/net/ipv4/Makefile 2014-11-21 03:51:00 +0000
@@ -54,6 +54,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.
obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
+obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o xfrm4_protocol.o
=== modified file 'net/ipv4/ip_output.c'
--- old/net/ipv4/ip_output.c 2014-11-21 03:17:49 +0000
+++ new/net/ipv4/ip_output.c 2014-11-21 03:51:00 +0000
@@ -1051,7 +1051,7 @@ alloc_new_skb:
__skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, 0);
skb_shinfo(skb)->nr_frags = ++i;
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
}
copy = min_t(int, copy, pfrag->size - pfrag->offset);
if (getfrag(from,
@@ -1276,7 +1276,7 @@ ssize_t ip_append_page(struct sock *sk,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len);
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
=== modified file 'net/ipv4/tcp.c'
--- old/net/ipv4/tcp.c 2014-11-21 03:17:49 +0000
+++ new/net/ipv4/tcp.c 2014-11-21 03:51:00 +0000
@@ -950,7 +950,7 @@ new_segment:
if (can_coalesce) {
skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
@@ -1251,7 +1251,7 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, copy);
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
}
pfrag->offset += copy;
}
=== added file 'net/ipv4/tcp_zero_copy.c'
--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000
+++ new/net/ipv4/tcp_zero_copy.c 2014-11-21 03:51:00 +0000
@@ -0,0 +1,50 @@
+/*
+ * Support routines for TCP zero copy transmit
+ *
+ * Created by Vladislav Bolkhovitin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/skbuff.h>
+
+net_get_page_callback_t net_get_page_callback __read_mostly;
+EXPORT_SYMBOL_GPL(net_get_page_callback);
+
+net_put_page_callback_t net_put_page_callback __read_mostly;
+EXPORT_SYMBOL_GPL(net_put_page_callback);
+
+/*
+ * Caller of this function must ensure that at the moment when it's called
+ * there are no pages in the system with net_priv field set to non-zero
+ * value. Hence, this function, as well as net_get_page() and net_put_page(),
+ * don't need any protection.
+ */
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks);
=== modified file 'net/ipv6/ip6_output.c'
--- old/net/ipv6/ip6_output.c 2014-11-21 03:17:49 +0000
+++ new/net/ipv6/ip6_output.c 2014-11-21 03:51:00 +0000
@@ -1477,7 +1477,7 @@ alloc_new_skb:
__skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, 0);
skb_shinfo(skb)->nr_frags = ++i;
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
}
copy = min_t(int, copy, pfrag->size - pfrag->offset);
if (getfrag(from,
@@ -0,0 +1,387 @@
Subject: [PATCH] put_page_callback
---
drivers/block/drbd/drbd_receiver.c | 2 +-
include/linux/mm_types.h | 11 +++++++++
include/linux/net.h | 40 ++++++++++++++++++++++++++++++
include/linux/skbuff.h | 4 +--
net/Kconfig | 12 +++++++++
net/ceph/pagevec.c | 2 +-
net/core/skbuff.c | 14 +++++------
net/core/sock.c | 4 +--
net/ipv4/Makefile | 1 +
net/ipv4/ip_output.c | 4 +--
net/ipv4/tcp.c | 4 +--
net/ipv4/tcp_zero_copy.c | 50 ++++++++++++++++++++++++++++++++++++++
net/ipv6/ip6_output.c | 2 +-
13 files changed, 132 insertions(+), 18 deletions(-)
create mode 100644 net/ipv4/tcp_zero_copy.c
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index 6960fb0..8fa4016 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -132,7 +132,7 @@ static int page_chain_free(struct page *page)
struct page *tmp;
int i = 0;
page_chain_for_each_safe(page, tmp) {
- put_page(page);
+ net_put_page(page);
++i;
}
return i;
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 6e0b286..5706a4d 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -196,6 +196,17 @@ struct page {
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
int _last_cpupid;
#endif
+
+#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completion. It might look as not good to have this field here and
+ * it's better to have it in struct sk_buff, but it would make the code
+ * much more complicated and fragile, since all skb then would have to
+ * contain only pages with the same value in this field.
+ */
+ void *net_priv;
+#endif
}
/*
* The struct page can be forced to be double word aligned so that atomic ops
diff --git a/include/linux/net.h b/include/linux/net.h
index 17d8339..f784384 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -19,6 +19,7 @@
#define _LINUX_NET_H
#include <linux/stringify.h>
+#include <linux/mm.h>
#include <linux/random.h>
#include <linux/wait.h>
#include <linux/fcntl.h> /* For O_CLOEXEC and O_NONBLOCK */
@@ -285,6 +286,45 @@ int kernel_sendpage(struct socket *sock, struct page *page, int offset,
int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how);
+#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+/* Support for notification on zero-copy TCP transfer completion */
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+/*
+ * See comment for net_set_get_put_page_callbacks() why those functions
+ * don't need any protection.
+ */
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+#else
+static inline void net_get_page(struct page *page)
+{
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ put_page(page);
+}
+#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */
+
#define MODULE_ALIAS_NETPROTO(proto) \
MODULE_ALIAS("net-pf-" __stringify(proto))
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 6c8b6f6..edf6195 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -2250,7 +2250,7 @@ static inline struct page *skb_frag_page(const skb_frag_t *frag)
*/
static inline void __skb_frag_ref(skb_frag_t *frag)
{
- get_page(skb_frag_page(frag));
+ net_get_page(skb_frag_page(frag));
}
/**
@@ -2273,7 +2273,7 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f)
*/
static inline void __skb_frag_unref(skb_frag_t *frag)
{
- put_page(skb_frag_page(frag));
+ net_put_page(skb_frag_page(frag));
}
/**
diff --git a/net/Kconfig b/net/Kconfig
index 99815b5..ac45213 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -76,6 +76,18 @@ config INET
Short answer: say Y.
+config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
+ bool "TCP/IP zero-copy transfer completion notification"
+ depends on INET
+ default SCST_ISCSI
+ ---help---
+ Adds support for sending a notification upon completion of a
+ zero-copy TCP/IP transfer. This can speed up certain TCP/IP
+ software. Currently this is only used by the iSCSI target driver
+ iSCSI-SCST.
+
+ If unsure, say N.
+
if INET
source "net/ipv4/Kconfig"
source "net/ipv6/Kconfig"
diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c
index 5550130..993f710 100644
--- a/net/ceph/pagevec.c
+++ b/net/ceph/pagevec.c
@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty)
for (i = 0; i < num_pages; i++) {
if (dirty)
set_page_dirty_lock(pages[i]);
- put_page(pages[i]);
+ net_put_page(pages[i]);
}
if (is_vmalloc_addr(pages))
vfree(pages);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 32e31c2..6eb3a9e 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -437,7 +437,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
if (likely(data)) {
skb = build_skb(data, fragsz);
if (unlikely(!skb))
- put_page(virt_to_head_page(data));
+ net_put_page(virt_to_head_page(data));
}
} else {
skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask,
@@ -495,7 +495,7 @@ static void skb_clone_fraglist(struct sk_buff *skb)
static void skb_free_head(struct sk_buff *skb)
{
if (skb->head_frag)
- put_page(virt_to_head_page(skb->head));
+ net_put_page(virt_to_head_page(skb->head));
else
kfree(skb->head);
}
@@ -822,7 +822,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
if (!page) {
while (head) {
struct page *next = (struct page *)page_private(head);
- put_page(head);
+ net_put_page(head);
head = next;
}
return -ENOMEM;
@@ -1669,7 +1669,7 @@ EXPORT_SYMBOL(skb_copy_bits);
*/
static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
{
- put_page(spd->pages[i]);
+ net_put_page(spd->pages[i]);
}
static struct page *linear_to_page(struct page *page, unsigned int *len,
@@ -1722,7 +1722,7 @@ static bool spd_fill_page(struct splice_pipe_desc *spd,
spd->partial[spd->nr_pages - 1].len += *len;
return false;
}
- get_page(page);
+ net_get_page(page);
spd->pages[spd->nr_pages] = page;
spd->partial[spd->nr_pages].len = *len;
spd->partial[spd->nr_pages].offset = offset;
@@ -2181,7 +2181,7 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen)
page = virt_to_head_page(from->head);
offset = from->data - (unsigned char *)page_address(page);
__skb_fill_page_desc(to, 0, page, offset, plen);
- get_page(page);
+ net_get_page(page);
j = 1;
len -= plen;
}
@@ -2835,7 +2835,7 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
copy);
frg_cnt++;
pfrag->offset += copy;
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
skb->truesize += copy;
atomic_add(copy, &sk->sk_wmem_alloc);
diff --git a/net/core/sock.c b/net/core/sock.c
index 15e0c67..e8ea0df 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1830,7 +1830,7 @@ bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t gfp)
}
if (pfrag->offset + sz <= pfrag->size)
return true;
- put_page(pfrag->page);
+ net_put_page(pfrag->page);
}
pfrag->offset = 0;
@@ -2581,7 +2581,7 @@ void sk_common_release(struct sock *sk)
sk_refcnt_debug_release(sk);
if (sk->sk_frag.page) {
- put_page(sk->sk_frag.page);
+ net_put_page(sk->sk_frag.page);
sk->sk_frag.page = NULL;
}
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 518c04e..4072a87 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
obj-$(CONFIG_GENEVE) += geneve.o
+obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o xfrm4_protocol.o
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index bc6471d..ab9e262 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1051,7 +1051,7 @@ alloc_new_skb:
__skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, 0);
skb_shinfo(skb)->nr_frags = ++i;
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
}
copy = min_t(int, copy, pfrag->size - pfrag->offset);
if (getfrag(from,
@@ -1276,7 +1276,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len);
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 38c2bcb..f089a7a 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -949,7 +949,7 @@ new_segment:
if (can_coalesce) {
skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
@@ -1250,7 +1250,7 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, copy);
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
}
pfrag->offset += copy;
}
diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c
new file mode 100644
index 0000000..430147e
--- /dev/null
+++ b/net/ipv4/tcp_zero_copy.c
@@ -0,0 +1,50 @@
+/*
+ * Support routines for TCP zero copy transmit
+ *
+ * Created by Vladislav Bolkhovitin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/skbuff.h>
+
+net_get_page_callback_t net_get_page_callback __read_mostly;
+EXPORT_SYMBOL_GPL(net_get_page_callback);
+
+net_put_page_callback_t net_put_page_callback __read_mostly;
+EXPORT_SYMBOL_GPL(net_put_page_callback);
+
+/*
+ * Caller of this function must ensure that at the moment when it's called
+ * there are no pages in the system with net_priv field set to non-zero
+ * value. Hence, this function, as well as net_get_page() and net_put_page(),
+ * don't need any protection.
+ */
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 8e950c2..8cb4760 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1472,7 +1472,7 @@ alloc_new_skb:
__skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, 0);
skb_shinfo(skb)->nr_frags = ++i;
- get_page(pfrag->page);
+ net_get_page(pfrag->page);
}
copy = min_t(int, copy, pfrag->size - pfrag->offset);
if (getfrag(from,
--
2.1.2
@@ -0,0 +1,453 @@
[PATCH] put_page_callback-2.6.32-504
---
include/linux/Kbuild | 1 +
include/linux/mm_types.h | 12 +++++++++++
include/linux/net.h | 40 +++++++++++++++++++++++++++++++++++++
net/Kconfig | 12 +++++++++++
net/core/dev.c | 2 +-
net/core/skbuff.c | 34 ++++++++++++++++----------------
net/ipv4/Makefile | 1 +
net/ipv4/ip_output.c | 4 +-
net/ipv4/tcp.c | 8 +++---
net/ipv4/tcp_output.c | 2 +-
net/ipv4/tcp_zero_copy.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/ip6_output.c | 2 +-
12 files changed, 141 insertions(+), 26 deletions(-)
create mode 100644 net/ipv4/tcp_zero_copy.c
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 9301043..2870f1a 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -113,6 +113,7 @@ header-y += map_to_7segment.h
header-y += matroxfb.h
header-y += meye.h
header-y += minix_fs.h
+header-y += mm.h
header-y += mmtimer.h
header-y += mqueue.h
header-y += mtio.h
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 645f205..7b6de1f 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -106,6 +106,18 @@ struct page {
*/
void *shadow;
#endif
+
+#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completion. It might look as not good to have this field here and
+ * it's better to have it in struct sk_buff, but it would make the code
+ * much more complicated and fragile, since all skb then would have to
+ * contain only pages with the same value in this field.
+ */
+ void *net_priv;
+#endif
+
};
/*
diff --git a/include/linux/net.h b/include/linux/net.h
index 58ada6b..b0adbdc 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -20,6 +20,7 @@
#include <linux/socket.h>
#include <asm/socket.h>
+#include <linux/mm.h>
#define NPROTO AF_MAX
@@ -389,5 +390,44 @@ static const struct proto_ops name##_ops = { \
extern struct ratelimit_state net_ratelimit_state;
#endif
+#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
+/* Support for notification on zero-copy TCP transfer completion */
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+/*
+ * See comment for net_set_get_put_page_callbacks() why those functions
+ * don't need any protection.
+ */
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+#else
+static inline void net_get_page(struct page *page)
+{
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ put_page(page);
+}
+#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
diff --git a/net/Kconfig b/net/Kconfig
index 1d9b405..eedbed6 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -72,6 +72,18 @@ config INET
Short answer: say Y.
+config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
+ bool "TCP/IP zero-copy transfer completion notification"
+ depends on INET
+ default SCST_ISCSI
+ ---help---
+ Adds support for sending a notification upon completion of a
+ zero-copy TCP/IP transfer. This can speed up certain TCP/IP
+ software. Currently this is only used by the iSCSI target driver
+ iSCSI-SCST.
+
+ If unsure, say N.
+
if INET
source "net/ipv4/Kconfig"
source "net/ipv6/Kconfig"
diff --git a/net/core/dev.c b/net/core/dev.c
index 61dce2f..25d0826 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3655,7 +3655,7 @@ pull:
skb_shinfo(skb)->frags[0].size -= grow;
if (unlikely(!skb_shinfo(skb)->frags[0].size)) {
- put_page(skb_shinfo(skb)->frags[0].page);
+ net_put_page(skb_shinfo(skb)->frags[0].page);
memmove(skb_shinfo(skb)->frags,
skb_shinfo(skb)->frags + 1,
--skb_shinfo(skb)->nr_frags);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 157dc11..74ac749 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -78,13 +78,13 @@ static struct kmem_cache *skbuff_fclone_cache __read_mostly;
static void sock_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
- put_page(buf->page);
+ net_put_page(buf->page);
}
static void sock_pipe_buf_get(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
- get_page(buf->page);
+ net_get_page(buf->page);
}
static int sock_pipe_buf_steal(struct pipe_inode_info *pipe,
@@ -396,7 +396,7 @@ static void skb_release_data(struct sk_buff *skb)
if (skb_shinfo(skb)->nr_frags) {
int i;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
}
/*
@@ -705,7 +705,7 @@ static int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
if (!page) {
while (head) {
struct page *next = (struct page *)head->private;
- put_page(head);
+ net_put_page(head);
head = next;
}
return -ENOMEM;
@@ -720,7 +720,7 @@ static int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
/* skb frags release userspace buffers */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
uarg->callback(uarg);
@@ -886,7 +886,7 @@ struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].page);
+ net_get_page(skb_shinfo(n)->frags[i].page);
}
skb_shinfo(n)->nr_frags = i;
}
@@ -967,7 +967,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
skb_tx(skb)->dev_zerocopy = 0;
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
if (skb_has_frag_list(skb))
skb_clone_fraglist(skb);
@@ -1246,7 +1246,7 @@ drop_pages:
skb_shinfo(skb)->nr_frags = i;
for (; i < nfrags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
if (skb_has_frag_list(skb))
skb_drop_fraglist(skb);
@@ -1415,7 +1415,7 @@ pull_pages:
k = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
@@ -1517,7 +1517,7 @@ EXPORT_SYMBOL(skb_copy_bits);
*/
static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
{
- put_page(spd->pages[i]);
+ net_put_page(spd->pages[i]);
}
static inline struct page *linear_to_page(struct page *page, unsigned int *len,
@@ -1541,7 +1541,7 @@ new_page:
off = sk->sk_sndmsg_off;
mlen = PAGE_SIZE - off;
if (mlen < 64 && mlen < *len) {
- put_page(p);
+ net_put_page(p);
goto new_page;
}
@@ -1551,7 +1551,7 @@ new_page:
memcpy(page_address(p) + off, page_address(page) + *offset, *len);
sk->sk_sndmsg_off += *len;
*offset = off;
- get_page(p);
+ net_get_page(p);
return p;
}
@@ -1572,7 +1572,7 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
if (!page)
return 1;
} else
- get_page(page);
+ net_get_page(page);
spd->pages[spd->nr_pages] = page;
spd->partial[spd->nr_pages].len = *len;
@@ -2202,7 +2202,7 @@ static inline void skb_split_no_header(struct sk_buff *skb,
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += len - pos;
skb_shinfo(skb1)->frags[0].size -= len - pos;
skb_shinfo(skb)->frags[i].size = len - pos;
@@ -2325,7 +2325,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
to++;
} else {
- get_page(fragfrom->page);
+ net_get_page(fragfrom->page);
fragto->page = fragfrom->page;
fragto->page_offset = fragfrom->page_offset;
fragto->size = todo;
@@ -2347,7 +2347,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
fragto = &skb_shinfo(tgt)->frags[merge];
fragto->size += fragfrom->size;
- put_page(fragfrom->page);
+ net_put_page(fragfrom->page);
}
/* Reposition in the original skb */
@@ -2760,7 +2760,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
while (pos < offset + len && i < nfrags) {
*frag = skb_shinfo(skb)->frags[i];
- get_page(frag->page);
+ net_get_page(frag->page);
size = frag->size;
if (pos < offset) {
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index e18daba..65f5371 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
+obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 7ac7cfa..8cda5cc 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1000,7 +1000,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
@@ -1239,7 +1239,7 @@ ssize_t ip_append_page(struct sock *sk, struct page *page,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_shinfo(skb)->frags[i-1].size += len;
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 18d22cf..b5c12fa 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -821,7 +821,7 @@ new_segment:
if (can_coalesce) {
skb_shinfo(skb)->frags[i - 1].size += copy;
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
skb_tx(skb)->shared_frag = 1;
@@ -1030,7 +1030,7 @@ new_segment:
goto new_segment;
} else if (page) {
if (off == PAGE_SIZE) {
- put_page(page);
+ net_put_page(page);
TCP_PAGE(sk) = page = NULL;
off = 0;
}
@@ -1071,9 +1071,9 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, page, off, copy);
if (TCP_PAGE(sk)) {
- get_page(page);
+ net_get_page(page);
} else if (off + copy < PAGE_SIZE) {
- get_page(page);
+ net_get_page(page);
TCP_PAGE(sk) = page;
}
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 255e6e3..5c48819 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1071,7 +1071,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)
k = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c
new file mode 100644
index 0000000..9cd990c
--- /dev/null
+++ b/net/ipv4/tcp_zero_copy.c
@@ -0,0 +1,49 @@
+/*
+ * Support routines for TCP zero copy transmit
+ *
+ * Created by Vladislav Bolkhovitin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/skbuff.h>
+
+net_get_page_callback_t net_get_page_callback __read_mostly;
+EXPORT_SYMBOL(net_get_page_callback);
+
+net_put_page_callback_t net_put_page_callback __read_mostly;
+EXPORT_SYMBOL(net_put_page_callback);
+
+/*
+ * Caller of this function must ensure that at the moment when it's called
+ * there are no pages in the system with net_priv field set to non-zero
+ * value. Hence, this function, as well as net_get_page() and net_put_page(),
+ * don't need any protection.
+ */
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(net_set_get_put_page_callbacks);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 0e985f7..22b4529 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1375,7 +1375,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
--
1.7.1
+3 -3
View File
@@ -374,7 +374,7 @@ int __del_session(struct iscsi_target *target, u64 sid)
if (!list_empty(&session->conn_list)) {
PRINT_ERROR("%llx still have connections",
(long long unsigned int)session->sid);
(unsigned long long int)session->sid);
return -EBUSY;
}
@@ -391,7 +391,7 @@ void iscsi_sess_force_close(struct iscsi_session *sess)
lockdep_assert_held(&sess->target->target_mutex);
PRINT_INFO("Deleting session %llx with initiator %s (%p)",
(long long unsigned int)sess->sid, sess->initiator_name, sess);
(unsigned long long int)sess->sid, sess->initiator_name, sess);
list_for_each_entry(conn, &sess->conn_list, conn_list_entry) {
TRACE_MGMT_DBG("Deleting connection with initiator %p", conn);
@@ -415,7 +415,7 @@ static void iscsi_session_info_show(struct seq_file *seq,
list_for_each_entry(session, &target->session_list,
session_list_entry) {
seq_printf(seq, "\tsid:%llx initiator:%s (reinstating %s)\n",
(long long unsigned int)session->sid,
(unsigned long long int)session->sid,
session->initiator_name,
session->sess_reinstating ? "yes" : "no");
conn_info_show(seq, session);
+1 -1
View File
@@ -1642,7 +1642,7 @@ static int mvst_handle_task_mgmt(struct mvs_info *mvi,
sess = mvst_find_sess_by_lid(tgt, initiator_sas_addr);
if (sess == NULL) {
TRACE(TRACE_MGMT, "mvsttgt(%ld): task mgmt fn 0x%x for "
"non-existant session", mvi->instance,
"non-existent session", mvi->instance,
cmd->save_task_iu.task_fun);
res = -EFAULT;
goto out;
+2 -2
View File
@@ -478,7 +478,7 @@ struct mvs_tgt_initiator {
};
/*
* Equivilant to IT Nexus (Initiator-Target)
* Equivalent to IT Nexus (Initiator-Target)
*/
struct mvst_sess {
struct list_head sess_entry;
@@ -558,7 +558,7 @@ struct mvst_tgt {
unsigned int tgt_enable_64bit_addr:1;
wait_queue_head_t waitQ;
int notify_ack_expected;
/* Count of sessions refering q2t_tgt, protected by hardware_lock */
/* Count of sessions referring q2t_tgt, protected by hardware_lock */
int sess_count;
struct list_head sess_list;
};
+10 -8
View File
@@ -3,21 +3,23 @@
ABT_DETAILS="x86_64"
ABT_JOBS=5
ABT_KERNELS=" \
3.16.7 \
3.18.3 \
3.17.8-nc \
3.16.7-nc \
3.15.10-nc \
3.14.23-nc \
3.14.29-nc \
3.13.11-nc \
3.12.31-nc \
3.12.36-nc \
3.11.10-nc \
3.10.59-nc \
3.10.65-nc \
3.9.11-nc \
3.8.13-nc \
3.7.10-nc \
3.6.11-nc \
3.5.7-nc \
3.4.103-nc \
3.4.105-nc \
3.3.8-nc \
3.2.61-nc \
3.2.66-nc \
3.1.10-nc \
3.0.101-nc \
2.6.39.4-nc \
@@ -26,9 +28,9 @@ ABT_KERNELS=" \
2.6.36.4-nc \
2.6.35.14 \
2.6.35.14-u-nc \
2.6.34.14-nc \
2.6.34.15-nc \
2.6.33.20-nc \
2.6.32.62-nc \
2.6.32.65-nc \
2.6.31.14-nc \
2.6.30.10-nc \
2.6.29.6-nc \
-7
View File
@@ -80,13 +80,6 @@ Instructions for obtaining a distribution-specific kernel source tree vary. An e
[root@proj src ]# tar xjf linux-source-`uname -r`.tar.bz2</pre>
</li>
<li>
Patch the kernel that has just been downloaded:
<pre>[root@proj src ]# ln -s linux-3.11 linux
[root@proj src ]# cd linux
[root@proj linux ]# patch -p1 &lt /root/scst/scst/kernel/scst_exec_req_fifo-3.11.patch</pre>
</li>
<li>The next step is to configure the kernel:
<pre>[root@proj linux ]# pwd
/usr/src/linux
@@ -0,0 +1,5 @@
ccflags-y += -Idrivers/scsi/qla2xxx
qla2x00tgt-y := qla2x00t.o
obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o
@@ -0,0 +1,5 @@
ccflags-y += -Idrivers/scsi/qla2xxx
qla2x00tgt-y := qla2x00t.o
obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o
+1 -1
View File
@@ -565,7 +565,7 @@ Thanks to:
initiator driver.
* Mark Buechler <mark.buechler@gmail.com> for the original
WWN-based authentification, a lot of useful suggestions, bug reports and
WWN-based authentication, a lot of useful suggestions, bug reports and
help in debugging.
* Ming Zhang <mingz@ele.uri.edu> for fixes.
+84 -32
View File
@@ -1547,6 +1547,8 @@ static int q2t_target_release(struct scst_tgt *scst_tgt)
q2t_target_stop(scst_tgt);
cancel_work_sync(&tgt->rscn_reg_work);
ha->q2t_tgt = NULL;
scst_tgt_set_tgt_priv(scst_tgt, NULL);
@@ -2409,8 +2411,8 @@ static void q2t_load_cont_data_segments(struct q2t_prm *prm)
*dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
TRACE_SG("S/G Segment Cont. phys_addr=%llx:%llx, len=%d",
(long long unsigned int)pci_dma_hi32(dma_addr),
(long long unsigned int)pci_dma_lo32(dma_addr),
(unsigned long long int)pci_dma_hi32(dma_addr),
(unsigned long long int)pci_dma_lo32(dma_addr),
(int)sg_dma_len(prm->sg));
prm->sg = __sg_next_inline(prm->sg);
@@ -2470,8 +2472,8 @@ static void q2x_load_data_segments(struct q2t_prm *prm)
*dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d",
(long long unsigned int)pci_dma_hi32(dma_addr),
(long long unsigned int)pci_dma_lo32(dma_addr),
(unsigned long long int)pci_dma_hi32(dma_addr),
(unsigned long long int)pci_dma_lo32(dma_addr),
(int)sg_dma_len(prm->sg));
prm->sg = __sg_next_inline(prm->sg);
@@ -2532,8 +2534,8 @@ static void q24_load_data_segments(struct q2t_prm *prm)
*dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d",
(long long unsigned int)pci_dma_hi32(dma_addr),
(long long unsigned int)pci_dma_lo32(dma_addr),
(unsigned long long int)pci_dma_hi32(dma_addr),
(unsigned long long int)pci_dma_lo32(dma_addr),
(int)sg_dma_len(prm->sg));
prm->sg = __sg_next_inline(prm->sg);
@@ -2679,6 +2681,31 @@ out_unlock_free_unmap:
goto out;
}
/*
* Convert sense buffer (byte array) to little endian format as required by
* qla24xx firmware.
*/
static void q24_copy_sense_buffer_to_ctio(ctio7_status1_entry_t *ctio,
uint8_t *sense_buf, unsigned int sense_buf_len)
{
uint32_t *src = (void *)sense_buf;
uint32_t *end = (void *)sense_buf + sense_buf_len;
uint8_t *p;
__be32 *dst = (void *)ctio->sense_data;
/*
* The sense buffer allocated by scst_alloc_sense() is zero-filled and
* has a length that is a multiple of four. This means that it is safe
* to access the bytes after the end of the sense buffer up to a
* boundary that is a multiple of four.
*/
for (p = (uint8_t *)end; ((uintptr_t)p & 3) != 0; p++)
WARN_ONCE(*p != 0, "sense_buf[%zd] = %d\n", p - sense_buf, *p);
for ( ; src < end; src++)
*dst++ = cpu_to_be32(*src);
}
static inline int q2t_need_explicit_conf(scsi_qla_host_t *ha,
struct q2t_cmd *cmd, int sending_sense)
{
@@ -2914,7 +2941,6 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
ctio->residual = cpu_to_le32(prm->residual);
ctio->scsi_status = cpu_to_le16(prm->rq_result);
if (scst_sense_valid(prm->sense_buffer)) {
int i;
ctio1 = (ctio7_status1_entry_t *)ctio;
if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) {
ctio1->flags |= cpu_to_le16(
@@ -2925,20 +2951,8 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
ctio1->flags |= cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
ctio1->scsi_status |= cpu_to_le16(SS_SENSE_LEN_VALID);
ctio1->sense_length = cpu_to_le16(prm->sense_buffer_len);
for (i = 0; i < prm->sense_buffer_len/4; i++)
((uint32_t *)ctio1->sense_data)[i] =
cpu_to_be32(((uint32_t *)prm->sense_buffer)[i]);
#if 0
if (unlikely((prm->sense_buffer_len % 4) != 0)) {
static int q;
if (q < 10) {
PRINT_INFO("qla2x00t(%ld): %d bytes of sense "
"lost", prm->tgt->ha->instance,
prm->sense_buffer_len % 4);
q++;
}
}
#endif
q24_copy_sense_buffer_to_ctio(ctio1, prm->sense_buffer,
prm->sense_buffer_len);
} else {
ctio1 = (ctio7_status1_entry_t *)ctio;
ctio1->flags &= ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
@@ -4074,7 +4088,7 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb)
if (sess == NULL) {
TRACE_MGMT_DBG("qla2x00t(%ld): task mgmt fn 0x%x for "
"non-existant session", ha->instance, fn);
"non-existent session", ha->instance, fn);
res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_TM, iocb,
IS_FWI2_CAPABLE(ha) ? sizeof(atio7_entry_t) :
sizeof(notify_entry_t));
@@ -4161,11 +4175,39 @@ out:
return res;
}
static void q2t_rscn_reg_work(struct work_struct *work)
{
struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, rscn_reg_work);
scsi_qla_host_t *ha = tgt->ha;
int ret;
TRACE_ENTRY();
if ((ha->host->active_mode & MODE_INITIATOR) == 0) {
/*
* The QLogic firmware and qla2xxx do not register for RSCNs in
* target-only mode, so do that explicitly.
*/
ret = qla2x00_send_change_request(ha, 0x3, ha->vp_idx);
if (ret != QLA_SUCCESS)
PRINT_INFO("qla2x00t(%ld): RSCN registration failed: "
"%#x (OK for non-fabric setups)",
ha->host_no, ret);
else
TRACE_MGMT_DBG("qla2x00t(%ld): RSCN registration succeeded",
ha->host_no);
}
TRACE_EXIT();
return;
}
/*
* pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire
*/
static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb)
{
struct q2t_tgt *tgt = ha->tgt;
int res = 1; /* send notify ack */
struct q2t_sess *sess;
int loop_id;
@@ -4177,6 +4219,13 @@ static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb)
switch (iocb->status_subcode) {
case ELS_PLOGI:
/*
* HACK. Let's do it on PLOGI, because seems there is no other
* simple place, from where it can be called. In the worst
* case, we will just reinstall RSCNs once again, it's harmless.
*/
schedule_work(&tgt->rscn_reg_work);
break;
case ELS_FLOGI:
case ELS_PRLI:
break;
@@ -5297,7 +5346,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
* command was sent between the abort request
* was received and processed. Unfortunately,
* the firmware has a silly requirement that
* all aborted exchanges must be explicitely
* all aborted exchanges must be explicitly
* terminated, otherwise it refuses to send
* responses for the abort requests. So, we
* have to (re)terminate the exchange and
@@ -5405,11 +5454,11 @@ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha,
case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
case MBA_ATIO_TRANSFER_ERR: /* ATIO Queue Transfer Error */
TRACE(TRACE_MGMT, "qla2x00t(%ld): System error async event %#x "
"occured", ha->instance, code);
"occurred", ha->instance, code);
break;
case MBA_LOOP_UP:
TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop up occured",
TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop up occurred",
ha->instance);
if (tgt->link_reinit_iocb_pending) {
q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0);
@@ -5418,28 +5467,28 @@ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha,
break;
case MBA_LIP_OCCURRED:
TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP occured", ha->instance);
TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP occurred", ha->instance);
break;
case MBA_LOOP_DOWN:
TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop down occured",
TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop down occurred",
ha->instance);
break;
case MBA_LIP_RESET:
TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset occured",
TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset occurred",
ha->instance);
break;
case MBA_PORT_UPDATE:
case MBA_RSCN_UPDATE:
TRACE_MGMT_DBG("qla2x00t(%ld): Port update async event %#x "
"occured", ha->instance, code);
"occurred", ha->instance, code);
/* .mark_all_devices_lost() is handled by the initiator driver */
break;
default:
TRACE(TRACE_MGMT, "qla2x00t(%ld): Async event %#x occured: "
TRACE(TRACE_MGMT, "qla2x00t(%ld): Async event %#x occurred: "
"ignoring (m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)",
ha->instance, code,
le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
@@ -5823,8 +5872,10 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd)
q2t_cleanup_hw_pending_cmd(ha, cmd);
scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_FATAL,
SCST_CONTEXT_THREAD);
/* It might be sporadic, hence retriable */
scst_set_cmd_error(scst_cmd,
SCST_LOAD_SENSE(scst_sense_internal_failure));
scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_SENSE_SET, SCST_CONTEXT_THREAD);
goto out_unlock;
} else if (cmd->state == Q2T_STATE_ABORTED) {
TRACE_MGMT_DBG("Force finishing aborted cmd %p (tag %d)",
@@ -5872,6 +5923,7 @@ static int q2t_add_target(scsi_qla_host_t *ha)
tgt->ha = ha;
init_waitqueue_head(&tgt->waitQ);
INIT_WORK(&tgt->rscn_reg_work, q2t_rscn_reg_work);
INIT_LIST_HEAD(&tgt->sess_list);
INIT_LIST_HEAD(&tgt->del_sess_list);
INIT_DELAYED_WORK(&tgt->sess_del_work, q2t_del_sess_work_fn);
+4 -2
View File
@@ -141,7 +141,9 @@ struct q2t_tgt {
*/
unsigned long tgt_stop; /* the driver is being stopped */
/* Count of sessions refering q2t_tgt. Protected by hardware_lock. */
struct work_struct rscn_reg_work;
/* Count of sessions referring q2t_tgt. Protected by hardware_lock. */
int sess_count;
/*
@@ -177,7 +179,7 @@ struct q2t_tgt {
};
/*
* Equivilant to IT Nexus (Initiator-Target)
* Equivalent to IT Nexus (Initiator-Target)
*/
struct q2t_sess {
uint16_t loop_id;
+1 -1
View File
@@ -2147,7 +2147,7 @@ qla2x00_configure_loop(scsi_qla_host_t *ha)
DEBUG3(printk("%s: exiting normally\n", __func__));
}
/* Restore state if a resync event occured during processing */
/* Restore state if a resync event occurred during processing */
if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+3 -3
View File
@@ -261,7 +261,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
* qla2x00_start_scsi() - Send a SCSI command to the ISP
* @sp: command to send to the ISP
*
* Returns non-zero if a failure occured, else zero.
* Returns non-zero if a failure occurred, else zero.
*/
int
qla2x00_start_scsi(srb_t *sp)
@@ -407,7 +407,7 @@ queuing_error:
*
* Can be called from both normal and interrupt context.
*
* Returns non-zero if a failure occured, else zero.
* Returns non-zero if a failure occurred, else zero.
*
* Hardware lock must be held on entrance. Might release it, then reacquire.
*/
@@ -671,7 +671,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
* qla24xx_start_scsi() - Send a SCSI command to the ISP
* @sp: command to send to the ISP
*
* Returns non-zero if a failure occured, else zero.
* Returns non-zero if a failure occurred, else zero.
*/
int
qla24xx_start_scsi(srb_t *sp)
+28 -28
View File
@@ -419,9 +419,9 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
break;
case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no,
DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", ha->host_no,
mb[1]));
qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]);
qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]);
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
@@ -488,7 +488,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
ha->host_no, mb[1]));
qla_printk(KERN_INFO, ha,
"LIP reset occured (%x).\n", mb[1]);
"LIP reset occurred (%x).\n", mb[1]);
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
@@ -781,8 +781,8 @@ qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
fcport->last_ramp_up = jiffies;
DEBUG2(qla_printk(KERN_INFO, fcport->ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
"scsi(%ld:%d:%d:%lld): Queue depth adjusted-up to %d.\n",
fcport->ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
sdev->queue_depth));
}
@@ -795,8 +795,8 @@ qla2x00_adjust_sdev_qdepth_down(struct scsi_device *sdev, void *data)
return;
DEBUG2(qla_printk(KERN_INFO, fcport->ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
"scsi(%ld:%d:%d:%lld): Queue depth adjusted-down to %d.\n",
fcport->ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
sdev->queue_depth));
}
@@ -1140,11 +1140,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (IS_FWI2_CAPABLE(ha))
sense_data += rsp_info_len;
if (rsp_info_len > 3 && rsp_info[3]) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol "
DEBUG2(printk("scsi(%ld:%d:%d:%lld) FCP I/O protocol "
"failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..."
"retrying command\n", ha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, rsp_info_len, rsp_info[0],
(unsigned long long)cp->device->lun, rsp_info_len, rsp_info[0],
rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4],
rsp_info[5], rsp_info[6], rsp_info[7]));
@@ -1178,11 +1178,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
((unsigned)(scsi_bufflen(cp) - resid) <
cp->underflow)) {
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"scsi(%ld:%d:%d:%lld): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
"error status.\n", ha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
(unsigned long long)cp->device->lun, resid,
scsi_bufflen(cp));
cp->result = DID_ERROR << 16;
@@ -1230,10 +1230,10 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
CMD_RESID_LEN(cp) = resid;
} else {
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d) UNDERRUN status detected "
"scsi(%ld:%d:%lld) UNDERRUN status detected "
"0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
"os_underflow=0x%x\n", ha->host_no,
cp->device->id, cp->device->lun, comp_status,
cp->device->id, (unsigned long long)cp->device->lun, comp_status,
scsi_status, resid_len, resid, cp->cmnd[0],
cp->underflow));
@@ -1277,11 +1277,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
* layer to retry it by reporting a bus busy.
*/
if (!(scsi_status & SS_RESIDUAL_UNDER)) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
DEBUG2(printk("scsi(%ld:%d:%d:%lld) Dropped "
"frame(s) detected (%x of %x bytes)..."
"retrying command.\n", ha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
(unsigned long long)cp->device->lun, resid,
scsi_bufflen(cp)));
cp->result = DID_BUS_BUSY << 16;
@@ -1292,11 +1292,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if ((unsigned)(scsi_bufflen(cp) - resid) <
cp->underflow) {
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"scsi(%ld:%d:%d:%lld): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
"error status.\n", ha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
(unsigned long long)cp->device->lun, resid,
scsi_bufflen(cp));
cp->result = DID_ERROR << 16;
@@ -1310,9 +1310,9 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
case CS_DATA_OVERRUN:
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n",
ha->host_no, cp->device->id, cp->device->lun, comp_status,
scsi_status));
"scsi(%ld:%d:%lld): OVERRUN status detected 0x%x-0x%x\n",
ha->host_no, cp->device->id, (unsigned long long)cp->device->lun,
comp_status, scsi_status));
DEBUG2(printk(KERN_INFO
"CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3],
@@ -1335,9 +1335,9 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
* Target with DID_NO_CONNECT ELSE Queue the IOs in the
* retry_queue.
*/
DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down "
DEBUG2(printk("scsi(%ld:%d:%lld): status_entry: Port Down "
"pid=%ld, compl status=0x%x, port state=0x%x\n",
ha->host_no, cp->device->id, cp->device->lun,
ha->host_no, cp->device->id, (unsigned long long)cp->device->lun,
cp->serial_number, comp_status,
atomic_read(&fcport->state)));
@@ -1373,17 +1373,17 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (IS_FWI2_CAPABLE(ha)) {
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected "
"scsi(%ld:%d:%d:%lld): TIMEOUT status detected "
"0x%x-0x%x\n", ha->host_no, cp->device->channel,
cp->device->id, cp->device->lun, comp_status,
scsi_status));
cp->device->id, (unsigned long long)cp->device->lun,
comp_status, scsi_status));
break;
}
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x "
"scsi(%ld:%d:%d:%lld): TIMEOUT status detected 0x%x-0x%x "
"sflags=%x.\n", ha->host_no, cp->device->channel,
cp->device->id, cp->device->lun, comp_status, scsi_status,
le16_to_cpu(sts->status_flags)));
cp->device->id, (unsigned long long)cp->device->lun, comp_status,
scsi_status, le16_to_cpu(sts->status_flags)));
/* Check to see if logout occurred. */
if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT))
+5 -4
View File
@@ -232,7 +232,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
DEBUG2_3_11(printk("%s(%ld): timeout schedule "
"isp_abort_needed.\n", __func__, ha->host_no));
qla_printk(KERN_WARNING, ha,
"Mailbox command timeout occured. Scheduling ISP "
"Mailbox command timeout occurred. Scheduling ISP "
"abort.\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
qla2xxx_wake_dpc(ha);
@@ -243,7 +243,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
DEBUG2_3_11(printk("%s(%ld): timeout calling "
"abort_isp\n", __func__, ha->host_no));
qla_printk(KERN_WARNING, ha,
"Mailbox command timeout occured. Issuing ISP "
"Mailbox command timeout occurred. Issuing ISP "
"abort.\n");
set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
@@ -3035,9 +3035,9 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
/*
* This command is implicitly executed by firmware during login for the
* physical hosts
* physical hosts if initiator mode is enabled.
*/
if (vp_idx == 0)
if (vp_idx == 0 && (ha->host->active_mode & MODE_INITIATOR))
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_SEND_CHANGE_REQUEST;
@@ -3058,6 +3058,7 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
return rval;
}
EXPORT_SYMBOL(qla2x00_send_change_request);
int
qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
+10 -10
View File
@@ -909,8 +909,8 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
if (!fcport)
return FAILED;
qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
ha->host_no, cmd->device->id, cmd->device->lun, name);
qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET ISSUED.\n",
ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name);
err = 0;
if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
@@ -926,14 +926,14 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
cmd->device->lun, type) != QLA_SUCCESS)
goto eh_reset_failed;
qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
ha->host_no, cmd->device->id, cmd->device->lun, name);
qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET SUCCEEDED.\n",
ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name);
return SUCCESS;
eh_reset_failed:
qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n",
ha->host_no, cmd->device->id, cmd->device->lun, name,
qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET FAILED: %s.\n",
ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name,
reset_errors[err]);
return FAILED;
}
@@ -1228,8 +1228,8 @@ static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth)
return;
DEBUG2(qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
ha->host_no, sdev->channel, sdev->id, sdev->lun,
"scsi(%ld:%d:%d:%lld): Queue depth adjusted-down to %d.\n",
ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
sdev->queue_depth));
}
@@ -1246,8 +1246,8 @@ static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth)
scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth);
DEBUG2(qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
ha->host_no, sdev->channel, sdev->id, sdev->lun,
"scsi(%ld:%d:%d:%lld): Queue depth adjusted-up to %d.\n",
ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
sdev->queue_depth));
}
+8 -7
View File
@@ -107,16 +107,16 @@ EOF
# passed via stdin and send the specialized patch to stdout.
function specialize_patch {
if [ "${enable_specialize}" = "true" ]; then
if [ "${generating_upstream_patch}" = "true" ]; then
scripts/filter-trace-entry-exit
else
cat
fi |
"$(dirname $0)/specialize-patch" \
${specialize_patch_options} \
-v kernel_version="${kver3}" \
-v SCSI_EXEC_REQ_FIFO_DEFINED="${scsi_exec_req_fifo_defined}" \
-v SCST_IO_CONTEXT="${scst_io_context}" \
| if [ "${generating_upstream_patch}" = "true" ]; then
scripts/filter-trace-entry-exit
else
cat
fi
-v SCST_IO_CONTEXT="${scst_io_context}"
else
cat
fi
@@ -282,6 +282,7 @@ for p in scst/kernel/*-${kver}.patch \
echo iscsi-scst/kernel/patches/*-${kver}.patch;
fi)
do
[ -e "$p" ] || continue
# Exclude the put_page_callback patch when command-line option -u has been
# specified since the current approach is not considered acceptable for
# upstream kernel inclusion. See also http://lkml.org/lkml/2008/12/11/213.
@@ -309,7 +310,7 @@ scst_07_pres="scst/src/scst_pres.h scst/src/scst_pres.c"
scst_08_sysfs="scst/src/scst_sysfs.c"
scst_09_debug="scst/include/scst_debug.h scst/src/scst_debug.c"
scst_proc="scst/src/scst_proc.c"
scst_10_sgv="scst/include/scst_sgv.h scst/src/scst_mem.h scst/src/scst_mem.c doc/sgv_cache.sgml"
scst_10_sgv="scst/include/scst_sgv.h scst/src/scst_mem.h scst/src/scst_mem.c doc/scst_pg.sgml"
scst_user="scst/include/scst_user.h scst/src/dev_handlers/scst_user.c"
scst_13_vdisk="scst/src/dev_handlers/scst_vdisk.c"
scst_14_tg="scst/src/scst_tg.c"
+8 -4
View File
@@ -4,15 +4,19 @@ usage() {
echo "Usage: $(basename $0) name version"
}
if [ $# != 2 ]; then
if [ $# -lt 2 ]; then
usage
exit 1
fi
scriptdir="$(dirname "$0")"
name="$1"
version="$2"
name="$1"; shift
version="$1"; shift
files="$*"
if [ -z "$files" ]; then
files=$($scriptdir/list-source-files)
fi
tar --owner=root --group=root --transform="s|^|$name-$version/|" \
-cjf $name-$version.tar.bz2 $($scriptdir/list-source-files) &&
-cjf $name-$version.tar.bz2 $files &&
ls -l $name-$version.tar.bz2
+44 -12
View File
@@ -220,14 +220,10 @@ cd SPECS
log "Copying SCST patches to the SOURCES directory"
cd ${rpmbuild_dir}/SOURCES
copy_best_matching_patch $scst_dir/scst/kernel/rhel/scst_exec_req_fifo scst_exec_req_fifo.patch ||
{
echo "No matching scst_exec_req_fifo patch found for kernel version $kver";
exit 1;
}
copy_best_matching_patch $scst_dir/scst/kernel/rhel/scst_exec_req_fifo scst_exec_req_fifo.patch
copy_best_matching_patch $scst_dir/iscsi-scst/kernel/patches/rhel/put_page_callback put_page_callback.patch ||
{
echo "No matching scst_exec_req_fifo patch found for kernel version $kver";
echo "No matching put_page_callback patch found for kernel version $kver";
exit 1;
}
@@ -300,7 +296,7 @@ diff -u SPECS/kernel.spec{.orig,}
Source82: config-s390x-debug
Source83: config-s390x-debug-rhel
+Patch200: scst_exec_req_fifo.patch
+#Patch200: scst_exec_req_fifo.patch
+Patch201: put_page_callback.patch
+
# empty final patch file to facilitate testing of kernel patches
@@ -310,7 +306,7 @@ diff -u SPECS/kernel.spec{.orig,}
# Dynamically generate kernel .config files from config-* files
make -f %{SOURCE20} VERSION=%{version} configs
+ApplyPatch scst_exec_req_fifo.patch
+#ApplyPatch scst_exec_req_fifo.patch
+ApplyPatch put_page_callback.patch
+
ApplyOptionalPatch linux-kernel-test.patch
@@ -339,7 +335,7 @@ diff -u SPECS/kernel.spec{.orig,}
Source82: config-generic
Source83: config-x86_64-debug-rhel
+Patch200: scst_exec_req_fifo.patch
+#Patch200: scst_exec_req_fifo.patch
+Patch201: put_page_callback.patch
+
# empty final patch file to facilitate testing of kernel patches
@@ -349,7 +345,7 @@ diff -u SPECS/kernel.spec{.orig,}
# Dynamically generate kernel .config files from config-* files
make -f %{SOURCE20} VERSION=%{version} configs
+ApplyPatch scst_exec_req_fifo.patch
+#ApplyPatch scst_exec_req_fifo.patch
+ApplyPatch put_page_callback.patch
+
ApplyOptionalPatch linux-kernel-test.patch
@@ -365,6 +361,42 @@ diff -u SPECS/kernel.spec{.orig,}
make ARCH=$Arch %{oldconfig_target} > /dev/null
echo "# $Arch" > configs/$i
EOF
elif [ ${kver#2.6.32-504.} != $kver ]; then
# RHEL/CentOS/SL 6.6
patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
diff -u SPECS/kernel.spec{.orig,}
--- kernel.spec.orig 2014-12-03 16:20:14.764118318 +0100
+++ kernel.spec 2014-12-03 16:21:36.606089530 +0100
@@ -610,6 +610,9 @@
Source85: config-powerpc64-debug-rhel
Source86: config-s390x-debug-rhel
+#Patch200: scst_exec_req_fifo.patch
+Patch201: put_page_callback.patch
+
# empty final patch file to facilitate testing of kernel patches
Patch999999: linux-kernel-test.patch
@@ -932,6 +935,9 @@
# Dynamically generate kernel .config files from config-* files
make -f %{SOURCE20} VERSION=%{version} configs
+#ApplyPatch scst_exec_req_fifo.patch
+ApplyPatch put_page_callback.patch
+
ApplyOptionalPatch linux-kernel-test.patch
# Any further pre-build tree manipulations happen here.
@@ -958,6 +964,8 @@
for i in *.config
do
mv $i .config
+ echo "CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION=y" >> .config
+ sed -i.tmp -e 's/^CONFIG_SCSI_QLA_FC=.*/CONFIG_SCSI_QLA_FC=n/' .config
Arch=`head -1 .config | cut -b 3-`
make ARCH=$Arch %{oldconfig_target} > /dev/null
echo "# $Arch" > configs/$i
EOF
elif [ ${kver#3.10.0-12[13]} != $kver ]; then
# RHEL/CentOS/SL 7.0
patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
@@ -382,7 +414,7 @@ patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
Source2000: cpupower.service
Source2001: cpupower.config
+Patch200: scst_exec_req_fifo.patch
+#Patch200: scst_exec_req_fifo.patch
+Patch201: put_page_callback.patch
+
# empty final patch to facilitate testing of kernel patches
@@ -392,7 +424,7 @@ patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
# Drop some necessary files from the source dir into the buildroot
cp $RPM_SOURCE_DIR/kernel-%{version}-*.config .
+ApplyPatch scst_exec_req_fifo.patch
+#ApplyPatch scst_exec_req_fifo.patch
+ApplyPatch put_page_callback.patch
+
ApplyOptionalPatch linux-kernel-test.patch
+8 -2
View File
@@ -251,7 +251,7 @@ function evaluate(stmnt, pattern, arg, op, result) {
sub(pattern, op[1] == 0 ? op[2] : op[1], stmnt)
}
pattern="\\((-*[0-9]+)\\)"
pattern="\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)"
while (match(stmnt, pattern, op) != 0)
{
sub(pattern, op[1], stmnt)
@@ -545,11 +545,17 @@ BEGIN {
reset_hunk_state_variables()
match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h)
}
else if (delete_next_blank_line && match($0, "^+$"))
else if (delete_next_blank_line && $0 == "+")
{
discard = 1
delete_next_blank_line = 0
}
else if (lines >= 2 && !match(line[lines-2], ":$") &&
line[lines-1] == "+\x9return;" && $0 == "+}")
{
line[lines-1] = $0
lines_less_added++
}
else
{
delete_next_blank_line = 0
+197 -39
View File
@@ -1,25 +1,66 @@
%define kmod_name scst
%define kver %{expand:%%(echo ${KVER:-$(uname -r)})}
%define kernel_rpm %{expand:%%( \
krpm="$(rpm -qf /boot/vmlinuz-%{kver} 2>/dev/null | \
grep -v 'is not owned by any package' | head -n 1)"; \
if [ -n "$krpm" ]; then \
echo "/boot/vmlinuz-%{kver}"; \
else \
echo "%{nil}"; \
fi \
# kversion: Kernel version as it appears under /lib/modules.
# The algorithm for setting the variable kversion is as follows:
# - If the variable kversion has been set, use its value.
# - If an RPM with the name kernel-headers exists (RHEL / CentOS), use the
# version number of the kernel that package is based on. This provides the
# version number when building on a koji build server.
# - Otherwise use the version number of the running kernel.
%{!?kversion:%define kversion %{expand:%%(
if rpm --quiet -q kernel-headers; then
rpm -q --qf '%%%%{version}-%%%%{release}.%%%%{arch}' \\
kernel-headers;
else
uname -r;
fi
)}}
%{echo:kversion=%{kversion}
}
# kernel_rpm: Name of the kernel RPM if the kernel is available as an RPM.
%if %{expand:%%(rpm --quiet -q kernel-headers ||
rpm --quiet -qf /lib/modules/%{kversion}/kernel/arch 2>/dev/null;
echo $((1-$?)))}
%define kernel_rpm %{expand:%%(
if rpm --quiet -q kernel-headers; then
echo kernel;
else
rpm -q --qf '%%%%{name}\\n' \\
"$(rpm -qf /lib/modules/%%{kversion}/kernel/arch | head -n1)";
fi
)}
%endif
# krpmver: Version of the kernel RPM. Not necessarily identical to %{kversion}.
%{?kernel_rpm:%define krpmver %{expand:%%(
if rpm --quiet -q %%{kernel_rpm}; then
rpm -q --qf '%%%%{version}-%%%%{release}\\n' "%%{kernel_rpm}";
else
rpm -q --qf '%%%%{version}-%%%%{release}\\n' kernel-headers;
fi |
head -n1
)}}
%{echo:krpmver=%{krpmver}
}
# kernel_devel_rpm: Name of the kernel development RPM.
%{?kernel_rpm:%define kernel_devel_rpm %{kernel_rpm}-devel}
# Version of the RPM that is being built.
%define rpm_version @rpm_version@
# Make command with or without flags.
%define make %{expand:%%(echo ${MAKE:-make})}
%define pkgrel 1
%define dkms_version %{rpm_version}-%{pkgrel}%{?dist}
Name: %{kmod_name}-%{kver}
Version: %{rpm_version}
Release: 1
Summary: SCST mid-layer kernel driver
Name: %{kmod_name}
Version: %{rpm_version}
Release: %{pkgrel}%{?dist}
Summary: SCST mid-layer kernel drivers
Group: System/Kernel
License: GPLv2
Vendor: http://scst.sourceforge.net/
URL: http://scst.sourceforge.net/
# Unfortunately the Red Hat / CentOS kernel-debug-devel RPM provides
# kernel-devel so a workaround is needed to match the kernel-devel RPM.
%{?kernel_rpm:Requires: %{kernel_rpm} = %{krpmver}}
BuildRequires: %{?kernel_rpm:%{kernel_rpm} = %{krpmver} %{kernel_devel_rpm} = %{krpmver}} gcc make
Source: %{kmod_name}-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
@@ -40,10 +81,26 @@ Authors:
--------
Vladislav Bolkhovitin, Bart Van Assche and others
%package userspace
Summary: SCST mid-layer user space software
Group: Development/Kernel
Requires: scst-dkms
%description userspace
A generic SCSI target subsystem for Linux that allows to convert any Linux
server into a sophisticated storage server. The three layers in SCST are the
target driver layer; the SCSI target core and the device handler layer. SCST
target drivers realize communication with an initiator and implement a storage
protocol like iSCSI, FC or SRP. SCST device handlers implement a SCSI
interface on top of local storage. Examples of such local storage are SCSI
RAID controller (dev_disk handler), block device (vdisk_blockio handler), file
(vdisk_fileio handler) or custom block device behavior implemented in user
space (scst_user).
%package devel
Summary: SCST mid-layer kernel driver development package
Group: Development/Kernel
AutoReqProv: no
BuildArch: noarch
%description devel
A generic SCSI target subsystem for Linux (SCST) that allows to convert
@@ -53,6 +110,25 @@ provide access to a local SCSI RAID controller (dev_disk), block device
(vdisk_blockio), file (vdisk_fileio) or custom block device behavior
implemented in user space (scst_user).
Authors:
--------
Vladislav Bolkhovitin, Bart Van Assche and others
%package dkms
Summary: DKMS-enabled SCST source code package
Group: System/Kernel
BuildArch: noarch
Requires(pre): dkms gcc make
Requires(post): dkms
%description dkms
A generic SCSI target subsystem for Linux (SCST) that allows to convert
any Linux server into a sophisticated storage server. SCST target drivers
implement protocols like iSCSI, FC or SRP. SCST device handlers either
provide access to a local SCSI RAID controller (dev_disk), block device
(vdisk_blockio), file (vdisk_fileio) or custom block device behavior
implemented in user space (scst_user).
Authors:
--------
Vladislav Bolkhovitin, Bart Van Assche and others
@@ -62,14 +138,14 @@ Authors:
%setup -q -n %{kmod_name}-%{version}
%build
export KVER=%{kver} PREFIX=%{_prefix}
export KVER=%{kversion} PREFIX=%{_prefix}
export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y
for d in scst fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do
%{make} -C $d
done
%install
export KVER=%{kver} PREFIX=%{_prefix} MANDIR=%{_mandir}
export KVER=%{kversion} PREFIX=%{_prefix} MANDIR=%{_mandir}
export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y
for d in scst; do
DESTDIR=%{buildroot} %{make} -C $d install
@@ -77,12 +153,72 @@ done
for d in fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do
DESTDIR=%{buildroot} INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install
done
rm -f %{buildroot}/lib/modules/%{kver}/[Mm]odule*
rm -f %{buildroot}/lib/modules/%{kversion}/[Mm]odule*
install -d -m 755 %{buildroot}/usr/src/%{kmod_name}-%{dkms_version}
(
cd %{buildroot}/usr/src/%{kmod_name}-%{dkms_version} &&
tar --strip-components=1 -xaf %{SOURCE0}
)
cat >%{buildroot}/usr/src/%{kmod_name}-%{dkms_version}/dkms.conf <<"EOF"
PACKAGE_VERSION="%{dkms_version}"
PACKAGE_NAME="%{kmod_name}"
AUTOINSTALL=yes
MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t/qla2x00-target && make -sC scst_local && make -sC srpt && cp */*.ko */*/*.ko */*/*/*.ko ."
CLEAN="make clean"
# Remove any existing ib_srpt.ko kernel modules
PRE_INSTALL="find /lib/modules/${kernelver} -name ib_srpt.ko -exec rm {} \;"
BUILT_MODULE_NAME[ 0]="fcst"
BUILT_MODULE_NAME[ 1]="ib_srpt"
BUILT_MODULE_NAME[ 2]="iscsi-scst"
BUILT_MODULE_NAME[ 3]="qla2x00tgt"
BUILT_MODULE_NAME[ 4]="qla2xxx_scst"
BUILT_MODULE_NAME[ 5]="scst"
BUILT_MODULE_NAME[ 6]="scst_local"
BUILT_MODULE_NAME[ 7]="scst_cdrom"
BUILT_MODULE_NAME[ 8]="scst_changer"
BUILT_MODULE_NAME[ 9]="scst_disk"
BUILT_MODULE_NAME[10]="scst_modisk"
BUILT_MODULE_NAME[11]="scst_processor"
BUILT_MODULE_NAME[12]="scst_raid"
BUILT_MODULE_NAME[13]="scst_tape"
BUILT_MODULE_NAME[14]="scst_user"
BUILT_MODULE_NAME[15]="scst_vdisk"
BUILT_MODULE_NAME[16]="isert-scst"
DEST_MODULE_LOCATION[ 0]="/extra"
DEST_MODULE_LOCATION[ 1]="/extra"
DEST_MODULE_LOCATION[ 2]="/extra"
DEST_MODULE_LOCATION[ 3]="/extra"
DEST_MODULE_LOCATION[ 4]="/extra"
DEST_MODULE_LOCATION[ 5]="/extra"
DEST_MODULE_LOCATION[ 6]="/extra"
DEST_MODULE_LOCATION[ 7]="/extra/dev_handlers"
DEST_MODULE_LOCATION[ 8]="/extra/dev_handlers"
DEST_MODULE_LOCATION[ 9]="/extra/dev_handlers"
DEST_MODULE_LOCATION[10]="/extra/dev_handlers"
DEST_MODULE_LOCATION[11]="/extra/dev_handlers"
DEST_MODULE_LOCATION[12]="/extra/dev_handlers"
DEST_MODULE_LOCATION[13]="/extra/dev_handlers"
DEST_MODULE_LOCATION[14]="/extra/dev_handlers"
DEST_MODULE_LOCATION[15]="/extra/dev_handlers"
DEST_MODULE_LOCATION[16]="/extra"
EOF
%clean
rm -rf %{buildroot}
%pre
# Remove any existing ib_srpt.ko kernel modules
find /lib/modules/%{kversion} -name ib_srpt.ko -exec rm {} \;
# Remove files installed by "make install"
rm -f /usr/local/man/man5/iscsi-scstd.conf.5
rm -f /usr/local/man/man8/iscsi-scst-adm.8
rm -f /usr/local/man/man8/iscsi-scstd.8
rm -f /usr/local/sbin/iscsi-scst-adm
rm -f /usr/local/sbin/iscsi-scstd
rm -rf /usr/local/include/scst
%pre userspace
# Remove files installed by "make install"
rm -f /usr/local/man/man5/iscsi-scstd.conf.5
rm -f /usr/local/man/man8/iscsi-scst-adm.8
@@ -90,33 +226,49 @@ rm -f /usr/local/man/man8/iscsi-scstd.8
rm -f /usr/local/sbin/iscsi-scst-adm
rm -f /usr/local/sbin/iscsi-scstd
rm -rf /usr/local/include/scst
# Remove existing ib_srpt.ko kernel modules
find /lib/modules/%{kver} -name ib_srpt.ko -exec rm {} \;
%post
/sbin/depmod -a %{kver}
/sbin/depmod -a %{kversion}
%post dkms
dkms add -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade
dkms build -m %{kmod_name} -v %{dkms_version}
dkms install -m %{kmod_name} -v %{dkms_version}
%preun dkms
dkms remove -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade --all
true
%files
%defattr(-,root,root)
%dir /lib/modules/%{kver}/extra
/lib/modules/%{kver}/extra/fcst.ko
/lib/modules/%{kver}/extra/ib_srpt.ko
/lib/modules/%{kver}/extra/iscsi-scst.ko
/lib/modules/%{kver}/extra/isert-scst.ko
/lib/modules/%{kver}/extra/qla2x00tgt.ko
/lib/modules/%{kver}/extra/qla2xxx_scst.ko
/lib/modules/%{kver}/extra/scst.ko
/lib/modules/%{kver}/extra/scst_local.ko
%dir /lib/modules/%{kver}/extra/dev_handlers
/lib/modules/%{kver}/extra/dev_handlers/scst_cdrom.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_changer.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_disk.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_modisk.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_processor.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_raid.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_tape.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_user.ko
/lib/modules/%{kver}/extra/dev_handlers/scst_vdisk.ko
%dir /lib/modules/%{kversion}/extra
/lib/modules/%{kversion}/extra/fcst.ko
/lib/modules/%{kversion}/extra/ib_srpt.ko
/lib/modules/%{kversion}/extra/iscsi-scst.ko
/lib/modules/%{kversion}/extra/isert-scst.ko
/lib/modules/%{kversion}/extra/qla2x00tgt.ko
/lib/modules/%{kversion}/extra/qla2xxx_scst.ko
/lib/modules/%{kversion}/extra/scst.ko
/lib/modules/%{kversion}/extra/scst_local.ko
%dir /lib/modules/%{kversion}/extra/dev_handlers
/lib/modules/%{kversion}/extra/dev_handlers/scst_cdrom.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_changer.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_disk.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_modisk.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_processor.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_raid.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_tape.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_user.ko
/lib/modules/%{kversion}/extra/dev_handlers/scst_vdisk.ko
%{_mandir}/man5/iscsi-scstd.conf.5.gz
%{_mandir}/man8/iscsi-scst-adm.8.gz
%{_mandir}/man8/iscsi-scstd.8.gz
%{_sbindir}/iscsi-scst-adm
%{_sbindir}/iscsi-scstd
%dir /var/lib/scst/pr
%dir /var/lib/scst/vdev_mode_pages
%files userspace
%{_mandir}/man5/iscsi-scstd.conf.5.gz
%{_mandir}/man8/iscsi-scst-adm.8.gz
%{_mandir}/man8/iscsi-scstd.8.gz
@@ -136,6 +288,12 @@ find /lib/modules/%{kver} -name ib_srpt.ko -exec rm {} \;
/usr/include/scst/scst_sgv.h
/usr/include/scst/scst_user.h
%files dkms
%defattr(-,root,root)
/usr/src/%{kmod_name}-%{dkms_version}/
%changelog
* Fri Jan 16 2015 Bart Van Assche <bart.vanassche@sandisk.com>
- Added DKMS support.
* Fri Nov 22 2013 Bart Van Assche <bvanassche@acm.org>
- Initial spec file.
+3 -1
View File
@@ -54,7 +54,9 @@ enable_proc:
cd $(SCST_DIR) && $(MAKE) $@
release-archive:
../scripts/generate-release-archive scst "$$(sed -n 's/^#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^\"]*\)\".*/\1/p' include/scst_const.h)"
../scripts/generate-release-archive scst \
"$$(sed -n 's/^#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^\"]*\)\".*/\1/p' include/scst_const.h)" \
$(shell list-source-files) ../scripts/rebuild-rhel-kernel-rpm
help:
@echo " all (the default) : make all"
+3 -17
View File
@@ -70,27 +70,13 @@ following patches for the kernel in the "kernel" subdirectory. All of
them are optional, so, if you don't need the corresponding
functionality, you may not apply them.
1. scst_exec_req_fifo-2.6.X.patch. This patch is necessary for
pass-through dev handlers, because in the mainstream kernels
scsi_do_req()/scsi_execute_async() work in LIFO order, instead of
expected and required FIFO. So SCST needs new functions
scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in the
kernel. This patch does that. You may not patch the kernel if you don't
need the pass-through support. Alternatively, you can define
CONFIG_SCST_STRICT_SERIALIZING compile option during the compilation
(see description below). Unfortunately, the CONFIG_SCST_STRICT_SERIALIZING
trick doesn't work on kernels starting from 2.6.30, because those
kernels don't have the required functionality (scsi_execute_async())
anymore. So, on them to have pass-through working you have to apply
scst_exec_req_fifo-2.6.X.patch.
2. readahead-2.6.X.patch. This patch fixes problem in Linux readahead
1. readahead-2.6.X.patch. This patch fixes problem in Linux readahead
subsystem and greatly improves performance for software RAIDs. See
http://sourceforge.net/mailarchive/forum.php?thread_name=a0272b440906030714g67eabc5k8f847fb1e538cc62%40mail.gmail.com&forum_name=scst-devel
thread for more details. It is included in the mainstream kernels 2.6.33
and 2.6.32.11.
3. readahead-context-2.6.X.patch. This is backported from 2.6.31 version
2. readahead-context-2.6.X.patch. This is backported from 2.6.31 version
of the context readahead patch http://lkml.org/lkml/2009/4/12/9, big
thanks to Wu Fengguang. This is a performance improvement patch. It is
included in the mainstream kernel 2.6.31.
@@ -1088,7 +1074,7 @@ Each vdisk_fileio's device has the following attributes in
reported in the SCSI device identification VPD page. If eui64_id has
been set the value of this attribute is reported as the EUI-64 ID. The
first three bytes of an EUI-64 ID are a so-called organizationally
unique identifier (OUI). The remaining bytes may be choosen by the
unique identifier (OUI). The remaining bytes may be chosen by the
organization that owns the OUI. For more information about OUIs, see
also http://standards.ieee.org/develop/regauth/oui/public.html.
+1 -1
View File
@@ -935,7 +935,7 @@ Each vdisk_fileio's device has the following attributes in
reported in the SCSI device identification VPD page. If eui64_id has
been set the value of this attribute is reported as the EUI-64 ID. The
first three bytes of an EUI-64 ID are a so-called organizationally
unique identifier (OUI). The remaining bytes may be choosen by the
unique identifier (OUI). The remaining bytes may be chosen by the
organization that owns the OUI. For more information about OUIs, see
also http://standards.ieee.org/develop/regauth/oui/public.html.
+15 -4
View File
@@ -128,6 +128,14 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap);
#define nr_cpu_ids NR_CPUS
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
/*
* See also patch "fix abuses of ptrdiff_t" (commit ID
* 142956af525002c5378e7d91d81a01189841a785).
*/
typedef unsigned long uintptr_t;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
#define cpumask_bits(maskp) ((maskp)->bits)
#ifdef CONFIG_CPUMASK_OFFSTACK
@@ -1404,7 +1412,7 @@ struct scst_dev_type {
int (*dev_done)(struct scst_cmd *cmd);
/*
* Called to notify dev hander that the command is about to be freed.
* Called to notify dev handler that the command is about to be freed.
*
* Could be called on IRQ context.
*
@@ -2505,7 +2513,7 @@ struct scst_device {
/*
* Set, if this device is being unregistered. Useful to let sysfs
* attributes know when they should exit immediatelly to prevent
* attributes know when they should exit immediately to prevent
* possible deadlocks with their device unregistration waiting for
* their kobj last put.
*/
@@ -4373,9 +4381,12 @@ static inline int cancel_delayed_work_sync(struct delayed_work *work)
#endif
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && defined(CONFIG_LOCKDEP)
extern struct lockdep_map scst_suspend_dep_map;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && \
defined(CONFIG_DEBUG_LOCK_ALLOC)
extern struct lockdep_map scst_suspend_dep_map;
#define scst_assert_activity_suspended() \
WARN_ON(debug_locks && !lock_is_held(&scst_suspend_dep_map))
#else
@@ -4848,7 +4859,7 @@ void scst_init_threads(struct scst_cmd_threads *cmd_threads);
void scst_deinit_threads(struct scst_cmd_threads *cmd_threads);
void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
void (*done)(void *data, char *sense, int result, int resid));
#endif
+2 -2
View File
@@ -292,7 +292,6 @@ static inline int scst_sense_response_code(const uint8_t *sense)
#define scst_sense_format_in_progress NOT_READY, 0x04, 0x04
#define scst_sense_tp_transitioning NOT_READY, 0x04, 0x0A
#define scst_sense_tp_unav NOT_READY, 0x04, 0x0C
#define scst_sense_not_ready NOT_READY, 0x04, 0x10
#define scst_sense_no_medium NOT_READY, 0x3a, 0
/* MEDIUM_ERROR is 3 */
@@ -300,7 +299,7 @@ static inline int scst_sense_response_code(const uint8_t *sense)
#define scst_sense_read_error MEDIUM_ERROR, 0x11, 0
/* HARDWARE_ERROR is 4 */
#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0
#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0 /* non-retriable */
/* ILLEGAL_REQUEST is 5 */
#define scst_sense_invalid_opcode ILLEGAL_REQUEST, 0x20, 0
@@ -335,6 +334,7 @@ static inline int scst_sense_response_code(const uint8_t *sense)
/* ABORTED_COMMAND is 0xb */
#define scst_sense_aborted_command ABORTED_COMMAND, 0x00, 0
#define scst_sense_internal_failure ABORTED_COMMAND, 0x44, 0 /* retriable */
/* MISCOMPARE is 0xe */
#define scst_sense_miscompare_error MISCOMPARE, 0x1D, 0
@@ -0,0 +1,13 @@
diff --git a/drivers/Kconfig b/drivers/Kconfig
index aa43b91..c96860e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig"
source "drivers/scsi/Kconfig"
+source "drivers/scst/Kconfig"
+
source "drivers/ata/Kconfig"
source "drivers/md/Kconfig"
@@ -0,0 +1,13 @@
diff --git a/drivers/Kconfig b/drivers/Kconfig
index aa43b91..c96860e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig"
source "drivers/scsi/Kconfig"
+source "drivers/scst/Kconfig"
+
source "drivers/ata/Kconfig"
source "drivers/md/Kconfig"
@@ -0,0 +1,14 @@
ccflags-y += -Wno-unused-parameter
obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
scst_vdisk.o scst_raid.o scst_processor.o scst_user.o
obj-$(CONFIG_SCST_DISK) += scst_disk.o
obj-$(CONFIG_SCST_TAPE) += scst_tape.o
obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o
obj-$(CONFIG_SCST_MODISK) += scst_modisk.o
obj-$(CONFIG_SCST_CHANGER) += scst_changer.o
obj-$(CONFIG_SCST_RAID) += scst_raid.o
obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o
obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o
obj-$(CONFIG_SCST_USER) += scst_user.o
@@ -0,0 +1,14 @@
ccflags-y += -Wno-unused-parameter
obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
scst_vdisk.o scst_raid.o scst_processor.o scst_user.o
obj-$(CONFIG_SCST_DISK) += scst_disk.o
obj-$(CONFIG_SCST_TAPE) += scst_tape.o
obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o
obj-$(CONFIG_SCST_MODISK) += scst_modisk.o
obj-$(CONFIG_SCST_CHANGER) += scst_changer.o
obj-$(CONFIG_SCST_RAID) += scst_raid.o
obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o
obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o
obj-$(CONFIG_SCST_USER) += scst_user.o
@@ -0,0 +1,12 @@
diff --git a/drivers/Makefile b/drivers/Makefile
index ab93de8..45077ec 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -128,6 +128,7 @@ obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_BCMA) += bcma/
obj-$(CONFIG_VHOST_RING) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/
+obj-$(CONFIG_SCST) += scst/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
#common clk code
@@ -0,0 +1,12 @@
diff --git a/drivers/Makefile b/drivers/Makefile
index ebee555..17f67ae 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -134,6 +134,7 @@ obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_BCMA) += bcma/
obj-$(CONFIG_VHOST_RING) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/
+obj-$(CONFIG_SCST) += scst/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
#common clk code
+13
View File
@@ -0,0 +1,13 @@
ccflags-y += -Wno-unused-parameter
scst-y += scst_main.o
scst-y += scst_pres.o
scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \
srpt/ scst_local/
+13
View File
@@ -0,0 +1,13 @@
ccflags-y += -Wno-unused-parameter
scst-y += scst_main.o
scst-y += scst_pres.o
scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \
srpt/ scst_local/
@@ -1,529 +0,0 @@
diff -upkr linux-2.6.32/block/blk-map.c linux-2.6.32/block/blk-map.c
--- linux-2.6.32/block/blk-map.c 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/block/blk-map.c 2011-05-17 20:56:18.341812997 -0400
@@ -5,6 +5,7 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res = 0;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.32/include/linux/blkdev.h linux-2.6.32/include/linux/blkdev.h
--- linux-2.6.32/include/linux/blkdev.h 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/include/linux/blkdev.h 2009-12-16 07:21:35.000000000 -0500
@@ -708,6 +708,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -812,6 +814,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.32/include/linux/scatterlist.h linux-2.6.32/include/linux/scatterlist.h
--- linux-2.6.32/include/linux/scatterlist.h 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/include/linux/scatterlist.h 2009-12-16 07:21:35.000000000 -0500
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.32/lib/scatterlist.c linux-2.6.32/lib/scatterlist.c
--- linux-2.6.32/lib/scatterlist.c 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/lib/scatterlist.c 2009-12-16 07:21:35.000000000 -0500
@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
@@ -1 +0,0 @@
../scst_exec_req_fifo-3.10.patch
@@ -1,524 +0,0 @@
diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/block/blk-map.c ./block/blk-map.c
--- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/block/blk-map.c 2014-07-16 20:25:31.000000000 +0200
+++ ./block/blk-map.c 2014-08-07 09:09:11.751302961 +0200
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/blkdev.h ./include/linux/blkdev.h
--- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/blkdev.h 2014-07-16 20:25:31.000000000 +0200
+++ ./include/linux/blkdev.h 2014-08-07 09:09:11.751302961 +0200
@@ -719,6 +719,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -838,6 +840,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/scatterlist.h ./include/linux/scatterlist.h
--- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/scatterlist.h 2014-07-16 20:25:31.000000000 +0200
+++ ./include/linux/scatterlist.h 2014-08-07 09:09:11.751302961 +0200
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -244,6 +245,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/lib/scatterlist.c ./lib/scatterlist.c
--- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/lib/scatterlist.c 2014-07-16 20:25:31.000000000 +0200
+++ ./lib/scatterlist.c 2014-08-07 09:09:11.751302961 +0200
@@ -628,3 +628,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-529
View File
@@ -1,529 +0,0 @@
diff -upkr linux-2.6.30/block/blk-map.c linux-2.6.30/block/blk-map.c
--- linux-2.6.30/block/blk-map.c 2009-06-09 23:05:27.000000000 -0400
+++ linux-2.6.30/block/blk-map.c 2011-05-17 21:03:29.661813000 -0400
@@ -5,6 +5,7 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -272,6 +273,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = rq->data = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = rq->data = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = 0;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.30/include/linux/blkdev.h linux-2.6.30/include/linux/blkdev.h
--- linux-2.6.30/include/linux/blkdev.h 2009-06-09 23:05:27.000000000 -0400
+++ linux-2.6.30/include/linux/blkdev.h 2009-08-12 11:48:06.000000000 -0400
@@ -704,6 +704,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -807,6 +809,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.30/include/linux/scatterlist.h linux-2.6.30/include/linux/scatterlist.h
--- linux-2.6.30/include/linux/scatterlist.h 2009-06-09 23:05:27.000000000 -0400
+++ linux-2.6.30/include/linux/scatterlist.h 2009-08-12 11:50:02.000000000 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.30/lib/scatterlist.c linux-2.6.30/lib/scatterlist.c
--- linux-2.6.30/lib/scatterlist.c 2009-06-09 23:05:27.000000000 -0400
+++ linux-2.6.30/lib/scatterlist.c 2009-08-12 11:56:04.000000000 -0400
@@ -485,3 +485,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-529
View File
@@ -1,529 +0,0 @@
diff -upkr linux-2.6.31/block/blk-map.c linux-2.6.31/block/blk-map.c
--- linux-2.6.31/block/blk-map.c 2009-09-09 18:13:59.000000000 -0400
+++ linux-2.6.31/block/blk-map.c 2011-05-17 21:05:32.669812993 -0400
@@ -5,6 +5,7 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.31/include/linux/blkdev.h linux-2.6.31/include/linux/blkdev.h
--- linux-2.6.31/include/linux/blkdev.h 2009-09-09 18:13:59.000000000 -0400
+++ linux-2.6.31/include/linux/blkdev.h 2009-09-23 06:17:33.000000000 -0400
@@ -699,6 +699,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -803,6 +805,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.31/include/linux/scatterlist.h linux-2.6.31/include/linux/scatterlist.h
--- linux-2.6.31/include/linux/scatterlist.h 2009-09-09 18:13:59.000000000 -0400
+++ linux-2.6.31/include/linux/scatterlist.h 2009-09-23 06:17:33.000000000 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.31/lib/scatterlist.c linux-2.6.31/lib/scatterlist.c
--- linux-2.6.31/lib/scatterlist.c 2009-09-09 18:13:59.000000000 -0400
+++ linux-2.6.31/lib/scatterlist.c 2009-09-23 06:17:33.000000000 -0400
@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-529
View File
@@ -1,529 +0,0 @@
diff -upkr linux-2.6.32/block/blk-map.c linux-2.6.32/block/blk-map.c
--- linux-2.6.32/block/blk-map.c 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/block/blk-map.c 2011-05-17 20:56:18.341812997 -0400
@@ -5,6 +5,7 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.32/include/linux/blkdev.h linux-2.6.32/include/linux/blkdev.h
--- linux-2.6.32/include/linux/blkdev.h 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/include/linux/blkdev.h 2009-12-16 07:21:35.000000000 -0500
@@ -708,6 +708,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -812,6 +814,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.32/include/linux/scatterlist.h linux-2.6.32/include/linux/scatterlist.h
--- linux-2.6.32/include/linux/scatterlist.h 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/include/linux/scatterlist.h 2009-12-16 07:21:35.000000000 -0500
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.32/lib/scatterlist.c linux-2.6.32/lib/scatterlist.c
--- linux-2.6.32/lib/scatterlist.c 2009-12-02 22:51:21.000000000 -0500
+++ linux-2.6.32/lib/scatterlist.c 2009-12-16 07:21:35.000000000 -0500
@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-529
View File
@@ -1,529 +0,0 @@
diff -upkr linux-2.6.33/block/blk-map.c linux-2.6.33/block/blk-map.c
--- linux-2.6.33/block/blk-map.c 2010-02-24 13:52:17.000000000 -0500
+++ linux-2.6.33/block/blk-map.c 2011-05-17 21:09:00.317812998 -0400
@@ -5,6 +5,7 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.33/include/linux/blkdev.h linux-2.6.33/include/linux/blkdev.h
--- linux-2.6.33/include/linux/blkdev.h 2010-02-24 13:52:17.000000000 -0500
+++ linux-2.6.33/include/linux/blkdev.h 2010-03-01 07:41:59.000000000 -0500
@@ -710,6 +710,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -825,6 +827,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.33/include/linux/scatterlist.h linux-2.6.33/include/linux/scatterlist.h
--- linux-2.6.33/include/linux/scatterlist.h 2010-02-24 13:52:17.000000000 -0500
+++ linux-2.6.33/include/linux/scatterlist.h 2010-03-01 07:41:59.000000000 -0500
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.33/lib/scatterlist.c linux-2.6.33/lib/scatterlist.c
--- linux-2.6.33/lib/scatterlist.c 2010-02-24 13:52:17.000000000 -0500
+++ linux-2.6.33/lib/scatterlist.c 2010-03-01 07:41:59.000000000 -0500
@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-530
View File
@@ -1,530 +0,0 @@
diff -upkr linux-2.6.34/block/blk-map.c linux-2.6.34/block/blk-map.c
--- linux-2.6.34/block/blk-map.c 2010-05-16 17:17:36.000000000 -0400
+++ linux-2.6.34/block/blk-map.c 2011-05-17 21:10:43.745812995 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +273,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.34/include/linux/blkdev.h linux-2.6.34/include/linux/blkdev.h
--- linux-2.6.34/include/linux/blkdev.h 2010-05-16 17:17:36.000000000 -0400
+++ linux-2.6.34/include/linux/blkdev.h 2010-05-24 06:51:22.000000000 -0400
@@ -713,6 +713,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -828,6 +830,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.34/include/linux/scatterlist.h linux-2.6.34/include/linux/scatterlist.h
--- linux-2.6.34/include/linux/scatterlist.h 2010-05-16 17:17:36.000000000 -0400
+++ linux-2.6.34/include/linux/scatterlist.h 2010-05-24 06:51:22.000000000 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.34/lib/scatterlist.c linux-2.6.34/lib/scatterlist.c
--- linux-2.6.34/lib/scatterlist.c 2010-05-16 17:17:36.000000000 -0400
+++ linux-2.6.34/lib/scatterlist.c 2010-05-24 06:51:22.000000000 -0400
@@ -494,3 +494,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-530
View File
@@ -1,530 +0,0 @@
diff -upkr linux-2.6.35/block/blk-map.c linux-2.6.35/block/blk-map.c
--- linux-2.6.35/block/blk-map.c 2010-08-01 18:11:14.000000000 -0400
+++ linux-2.6.35/block/blk-map.c 2011-05-17 21:12:23.125813000 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +273,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= 1 << BIO_RW;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.35/include/linux/blkdev.h linux-2.6.35/include/linux/blkdev.h
--- linux-2.6.35/include/linux/blkdev.h 2010-08-01 18:11:14.000000000 -0400
+++ linux-2.6.35/include/linux/blkdev.h 2010-08-04 04:21:59.737128732 -0400
@@ -717,6 +717,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -832,6 +834,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.35/include/linux/scatterlist.h linux-2.6.35/include/linux/scatterlist.h
--- linux-2.6.35/include/linux/scatterlist.h 2010-08-01 18:11:14.000000000 -0400
+++ linux-2.6.35/include/linux/scatterlist.h 2010-08-04 04:21:59.741129485 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.35/lib/scatterlist.c linux-2.6.35/lib/scatterlist.c
--- linux-2.6.35/lib/scatterlist.c 2010-08-01 18:11:14.000000000 -0400
+++ linux-2.6.35/lib/scatterlist.c 2010-08-04 04:21:59.741129485 -0400
@@ -494,3 +494,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-532
View File
@@ -1,532 +0,0 @@
diff -upkr linux-2.6.36/block/blk-map.c linux-2.6.36/block/blk-map.c
--- linux-2.6.36/block/blk-map.c 2010-10-20 16:30:22.000000000 -0400
+++ linux-2.6.36/block/blk-map.c 2011-05-17 21:13:42.301812997 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -271,6 +273,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.36/include/linux/blkdev.h linux-2.6.36/include/linux/blkdev.h
--- linux-2.6.36/include/linux/blkdev.h 2010-10-20 16:30:22.000000000 -0400
+++ linux-2.6.36/include/linux/blkdev.h 2010-10-26 04:00:15.899759399 -0400
@@ -629,6 +629,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -746,6 +748,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.36/include/linux/scatterlist.h linux-2.6.36/include/linux/scatterlist.h
--- linux-2.6.36/include/linux/scatterlist.h 2010-10-20 16:30:22.000000000 -0400
+++ linux-2.6.36/include/linux/scatterlist.h 2010-10-26 04:00:15.899759399 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.36/lib/scatterlist.c linux-2.6.36/lib/scatterlist.c
--- linux-2.6.36/lib/scatterlist.c 2010-10-20 16:30:22.000000000 -0400
+++ linux-2.6.36/lib/scatterlist.c 2010-10-26 04:00:15.899759399 -0400
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-532
View File
@@ -1,532 +0,0 @@
diff -upkr linux-2.6.37/block/blk-map.c linux-2.6.37/block/blk-map.c
--- linux-2.6.37/block/blk-map.c 2011-01-04 19:50:19.000000000 -0500
+++ linux-2.6.37/block/blk-map.c 2011-05-17 21:15:14.329812999 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.37/include/linux/blkdev.h linux-2.6.37/include/linux/blkdev.h
--- linux-2.6.37/include/linux/blkdev.h 2011-01-04 19:50:19.000000000 -0500
+++ linux-2.6.37/include/linux/blkdev.h 2011-01-08 08:45:54.350430208 -0500
@@ -592,6 +592,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -709,6 +711,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.37/include/linux/scatterlist.h linux-2.6.37/include/linux/scatterlist.h
--- linux-2.6.37/include/linux/scatterlist.h 2011-01-04 19:50:19.000000000 -0500
+++ linux-2.6.37/include/linux/scatterlist.h 2011-01-08 08:45:54.354431761 -0500
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.37/lib/scatterlist.c linux-2.6.37/lib/scatterlist.c
--- linux-2.6.37/lib/scatterlist.c 2011-01-04 19:50:19.000000000 -0500
+++ linux-2.6.37/lib/scatterlist.c 2011-01-08 08:45:54.401930472 -0500
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-532
View File
@@ -1,532 +0,0 @@
diff -upkr linux-2.6.38/block/blk-map.c linux-2.6.38/block/blk-map.c
--- linux-2.6.38/block/blk-map.c 2011-03-14 21:20:32.000000000 -0400
+++ linux-2.6.38/block/blk-map.c 2011-05-11 22:07:37.589813000 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.38/include/linux/blkdev.h linux-2.6.38/include/linux/blkdev.h
--- linux-2.6.38/include/linux/blkdev.h 2011-03-14 21:20:32.000000000 -0400
+++ linux-2.6.38/include/linux/blkdev.h 2011-03-18 10:19:00.000000000 -0400
@@ -593,6 +593,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -709,6 +711,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.38/include/linux/scatterlist.h linux-2.6.38/include/linux/scatterlist.h
--- linux-2.6.38/include/linux/scatterlist.h 2011-03-14 21:20:32.000000000 -0400
+++ linux-2.6.38/include/linux/scatterlist.h 2011-03-18 10:19:00.000000000 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.38/lib/scatterlist.c linux-2.6.38/lib/scatterlist.c
--- linux-2.6.38/lib/scatterlist.c 2011-03-14 21:20:32.000000000 -0400
+++ linux-2.6.38/lib/scatterlist.c 2011-03-18 10:46:41.000000000 -0400
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-532
View File
@@ -1,532 +0,0 @@
diff -upkr linux-2.6.39/block/blk-map.c linux-2.6.39/block/blk-map.c
--- linux-2.6.39/block/blk-map.c 2011-05-19 00:06:34.000000000 -0400
+++ linux-2.6.39/block/blk-map.c 2011-05-19 10:49:02.753812997 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-2.6.39/include/linux/blkdev.h linux-2.6.39/include/linux/blkdev.h
--- linux-2.6.39/include/linux/blkdev.h 2011-05-19 00:06:34.000000000 -0400
+++ linux-2.6.39/include/linux/blkdev.h 2011-05-19 10:49:02.753812997 -0400
@@ -592,6 +592,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -707,6 +709,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-2.6.39/include/linux/scatterlist.h linux-2.6.39/include/linux/scatterlist.h
--- linux-2.6.39/include/linux/scatterlist.h 2011-05-19 00:06:34.000000000 -0400
+++ linux-2.6.39/include/linux/scatterlist.h 2011-05-19 10:49:02.753812997 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-2.6.39/lib/scatterlist.c linux-2.6.39/lib/scatterlist.c
--- linux-2.6.39/lib/scatterlist.c 2011-05-19 00:06:34.000000000 -0400
+++ linux-2.6.39/lib/scatterlist.c 2011-05-19 10:49:02.753812997 -0400
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-532
View File
@@ -1,532 +0,0 @@
diff -upkr linux-3.0.0-orig/block/blk-map.c linux-3.0.0-scst-dbg/block/blk-map.c
--- linux-3.0.0-orig/block/blk-map.c 2011-07-21 22:17:23.000000000 -0400
+++ linux-3.0.0-scst-dbg/block/blk-map.c 2011-07-22 19:40:27.131230804 -0400
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff -upkr linux-3.0.0-orig/include/linux/blkdev.h linux-3.0.0-scst-dbg/include/linux/blkdev.h
--- linux-3.0.0-orig/include/linux/blkdev.h 2011-07-21 22:17:23.000000000 -0400
+++ linux-3.0.0-scst-dbg/include/linux/blkdev.h 2011-07-22 19:24:27.803231156 -0400
@@ -594,6 +594,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -709,6 +711,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff -upkr linux-3.0.0-orig/include/linux/scatterlist.h linux-3.0.0-scst-dbg/include/linux/scatterlist.h
--- linux-3.0.0-orig/include/linux/scatterlist.h 2011-07-21 22:17:23.000000000 -0400
+++ linux-3.0.0-scst-dbg/include/linux/scatterlist.h 2011-07-22 19:24:27.803231156 -0400
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff -upkr linux-3.0.0-orig/lib/scatterlist.c linux-3.0.0-scst-dbg/lib/scatterlist.c
--- linux-3.0.0-orig/lib/scatterlist.c 2011-07-21 22:17:23.000000000 -0400
+++ linux-3.0.0-scst-dbg/lib/scatterlist.c 2011-07-22 19:40:27.131230804 -0400
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-536
View File
@@ -1,536 +0,0 @@
=== modified file 'linux-3.1-scst/block/blk-map.c'
--- linux-3.1-orig/block/blk-map.c 2011-10-26 20:34:50 +0000
+++ linux-3.1-scst/block/blk-map.c 2011-10-26 20:58:56 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'linux-3.1-scst/include/linux/blkdev.h'
--- linux-3.1-orig/include/linux/blkdev.h 2011-10-26 20:34:50 +0000
+++ linux-3.1-scst/include/linux/blkdev.h 2011-10-26 20:58:56 +0000
@@ -599,6 +599,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -714,6 +716,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'linux-3.1-scst/include/linux/scatterlist.h'
--- linux-3.1-orig/include/linux/scatterlist.h 2011-10-26 20:34:50 +0000
+++ linux-3.1-scst/include/linux/scatterlist.h 2011-10-26 20:58:56 +0000
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'linux-3.1-scst/lib/scatterlist.c'
--- linux-3.1-orig/lib/scatterlist.c 2011-10-26 20:34:50 +0000
+++ linux-3.1-scst/lib/scatterlist.c 2011-10-26 20:58:56 +0000
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-527
View File
@@ -1,527 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2013-07-23 02:45:53 +0000
+++ new/block/blk-map.c 2013-07-23 02:50:11 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2013-07-23 02:45:53 +0000
+++ new/include/linux/blkdev.h 2013-07-23 02:50:11 +0000
@@ -676,6 +676,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -795,6 +797,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2013-07-23 02:45:53 +0000
+++ new/include/linux/scatterlist.h 2013-07-23 02:50:11 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -244,6 +245,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2013-07-23 02:45:53 +0000
+++ new/lib/scatterlist.c 2013-07-23 02:50:11 +0000
@@ -627,3 +627,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-528
View File
@@ -1,528 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2013-09-28 00:14:38 +0000
+++ new/block/blk-map.c 2013-09-28 00:23:26 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2013-09-28 00:14:38 +0000
+++ new/include/linux/blkdev.h 2013-09-28 00:23:26 +0000
@@ -676,6 +676,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -795,6 +797,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2013-09-28 00:14:38 +0000
+++ new/include/linux/scatterlist.h 2013-09-28 00:23:26 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2013-09-28 00:14:38 +0000
+++ new/lib/scatterlist.c 2013-09-28 00:23:26 +0000
@@ -716,3 +716,127 @@ size_t sg_pcopy_to_buffer(struct scatter
return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
}
EXPORT_SYMBOL(sg_pcopy_to_buffer);
+
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-528
View File
@@ -1,528 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2013-11-30 00:34:22 +0000
+++ new/block/blk-map.c 2013-11-30 00:39:53 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2013-11-30 00:34:22 +0000
+++ new/include/linux/blkdev.h 2013-11-30 00:39:53 +0000
@@ -676,6 +676,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -795,6 +797,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2013-11-30 00:34:22 +0000
+++ new/include/linux/scatterlist.h 2013-11-30 00:39:53 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2013-11-30 00:34:22 +0000
+++ new/lib/scatterlist.c 2013-11-30 00:39:53 +0000
@@ -717,3 +717,127 @@ size_t sg_pcopy_to_buffer(struct scatter
return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
}
EXPORT_SYMBOL(sg_pcopy_to_buffer);
+
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-528
View File
@@ -1,528 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2014-01-30 00:25:53 +0000
+++ new/block/blk-map.c 2014-01-30 00:44:50 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2014-01-30 00:25:53 +0000
+++ new/include/linux/blkdev.h 2014-01-30 00:44:50 +0000
@@ -712,6 +712,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -831,6 +833,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2014-01-30 00:25:53 +0000
+++ new/include/linux/scatterlist.h 2014-01-30 00:44:50 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2014-01-30 00:25:53 +0000
+++ new/lib/scatterlist.c 2014-01-30 00:44:50 +0000
@@ -717,3 +717,127 @@ size_t sg_pcopy_to_buffer(struct scatter
return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
}
EXPORT_SYMBOL(sg_pcopy_to_buffer);
+
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-528
View File
@@ -1,528 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2014-04-17 22:02:06 +0000
+++ new/block/blk-map.c 2014-04-17 22:08:48 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2014-04-17 22:02:06 +0000
+++ new/include/linux/blkdev.h 2014-04-17 22:08:48 +0000
@@ -705,6 +705,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -825,6 +827,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2014-04-17 22:02:06 +0000
+++ new/include/linux/scatterlist.h 2014-04-17 22:08:48 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2014-04-17 22:02:06 +0000
+++ new/lib/scatterlist.c 2014-04-17 22:08:48 +0000
@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter
return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
}
EXPORT_SYMBOL(sg_pcopy_to_buffer);
+
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-528
View File
@@ -1,528 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2014-06-18 01:32:48 +0000
+++ new/block/blk-map.c 2014-06-18 01:40:34 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2014-06-18 01:32:48 +0000
+++ new/include/linux/blkdev.h 2014-06-18 01:40:34 +0000
@@ -717,6 +717,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -837,6 +839,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, const struct sg_iovec *,
int, unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2014-06-18 01:32:48 +0000
+++ new/include/linux/scatterlist.h 2014-06-18 01:40:34 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2014-06-18 01:32:48 +0000
+++ new/lib/scatterlist.c 2014-06-18 01:40:34 +0000
@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter
return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
}
EXPORT_SYMBOL(sg_pcopy_to_buffer);
+
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-524
View File
@@ -1,524 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2014-08-19 01:00:36 +0000
+++ new/block/blk-map.c 2014-08-19 01:37:01 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -273,6 +275,333 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2014-08-19 01:00:36 +0000
+++ new/include/linux/blkdev.h 2014-08-19 01:06:48 +0000
@@ -735,6 +735,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -856,6 +858,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, const struct sg_iovec *,
int, unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2014-08-19 01:00:36 +0000
+++ new/include/linux/scatterlist.h 2014-08-19 01:06:48 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2014-08-19 01:00:36 +0000
+++ new/lib/scatterlist.c 2014-08-19 01:06:48 +0000
@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter
return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
}
EXPORT_SYMBOL(sg_pcopy_to_buffer);
+
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-536
View File
@@ -1,536 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2012-01-10 22:58:17 +0000
+++ new/block/blk-map.c 2012-01-10 23:01:21 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2012-01-10 22:58:17 +0000
+++ new/include/linux/blkdev.h 2012-01-10 23:01:21 +0000
@@ -599,6 +599,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -716,6 +718,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2012-01-10 22:58:17 +0000
+++ new/include/linux/scatterlist.h 2012-01-10 23:01:21 +0000
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2012-01-10 22:58:17 +0000
+++ new/lib/scatterlist.c 2012-01-10 23:01:21 +0000
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-536
View File
@@ -1,536 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2012-03-19 23:46:01 +0000
+++ new/block/blk-map.c 2012-03-20 00:10:37 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,339 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
+ KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy,
+ KM_USER0, KM_USER1);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2012-03-19 23:46:01 +0000
+++ new/include/linux/blkdev.h 2012-03-20 00:10:37 +0000
@@ -612,6 +612,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -731,6 +733,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2012-03-19 23:46:01 +0000
+++ new/include/linux/scatterlist.h 2012-03-20 00:10:37 +0000
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
+#include <asm/kmap_types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/io.h>
@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2012-03-19 23:46:01 +0000
+++ new/lib/scatterlist.c 2012-03-20 00:10:37 +0000
@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page +
+ (src_offs >> PAGE_SHIFT), s_km_type) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page +
+ (dst_offs >> PAGE_SHIFT), d_km_type) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr, s_km_type);
+ kunmap_atomic(daddr, d_km_type);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ * @d_km_type: kmap_atomic type for the destination SG
+ * @s_km_type: kmap_atomic type for the source SG
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len,
+ enum km_type d_km_type, enum km_type s_km_type)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len, d_km_type, s_km_type);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-528
View File
@@ -1,528 +0,0 @@
diff --git a/block/blk-map.c b/block/blk-map.c
index 623e1cd..20349d0 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 4d4ac24..3fa6a30 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -609,6 +609,8 @@ extern unsigned long blk_max_low_pfn, blk_max_pfn;
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -728,6 +730,9 @@ extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, uns
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index ac9586d..4b743d7 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -220,6 +221,9 @@ size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 6096e89..1786ca9 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -517,3 +517,126 @@ size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-527
View File
@@ -1,527 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2012-08-08 02:57:29 +0000
+++ new/block/blk-map.c 2012-08-08 03:02:56 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2012-08-08 02:57:29 +0000
+++ new/include/linux/blkdev.h 2012-08-08 03:02:56 +0000
@@ -627,6 +627,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -746,6 +748,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2012-08-08 02:57:29 +0000
+++ new/include/linux/scatterlist.h 2012-08-08 03:02:56 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -220,6 +221,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2012-08-08 02:57:29 +0000
+++ new/lib/scatterlist.c 2012-08-08 03:02:56 +0000
@@ -517,3 +517,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-527
View File
@@ -1,527 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2012-10-01 18:39:34 +0000
+++ new/block/blk-map.c 2012-10-01 20:50:07 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2012-10-01 18:39:34 +0000
+++ new/include/linux/blkdev.h 2012-10-01 18:45:47 +0000
@@ -638,6 +638,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -757,6 +759,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2012-10-01 18:39:34 +0000
+++ new/include/linux/scatterlist.h 2012-10-01 18:45:47 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -224,6 +225,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2012-10-01 18:39:34 +0000
+++ new/lib/scatterlist.c 2012-10-01 20:50:07 +0000
@@ -573,3 +573,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-527
View File
@@ -1,527 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2012-12-17 19:41:04 +0000
+++ new/block/blk-map.c 2012-12-17 22:29:54 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2012-12-17 19:41:04 +0000
+++ new/include/linux/blkdev.h 2012-12-17 22:29:54 +0000
@@ -660,6 +660,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -779,6 +781,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2012-12-17 19:41:04 +0000
+++ new/include/linux/scatterlist.h 2012-12-17 22:29:54 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -225,6 +226,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2012-12-17 19:41:04 +0000
+++ new/lib/scatterlist.c 2012-12-17 22:29:54 +0000
@@ -592,3 +592,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-527
View File
@@ -1,527 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2013-02-22 21:12:31 +0000
+++ new/block/blk-map.c 2013-02-23 00:07:57 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2013-02-22 21:12:31 +0000
+++ new/include/linux/blkdev.h 2013-02-22 21:21:51 +0000
@@ -668,6 +668,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -787,6 +789,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2013-02-22 21:12:31 +0000
+++ new/include/linux/scatterlist.h 2013-02-22 21:21:51 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -225,6 +226,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2013-02-22 21:12:31 +0000
+++ new/lib/scatterlist.c 2013-02-23 00:07:57 +0000
@@ -593,3 +593,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
-527
View File
@@ -1,527 +0,0 @@
=== modified file 'block/blk-map.c'
--- old/block/blk-map.c 2013-05-11 05:39:14 +0000
+++ new/block/blk-map.c 2013-05-14 01:25:01 +0000
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include "blk.h"
@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
}
EXPORT_SYMBOL(blk_rq_unmap_user);
+struct blk_kern_sg_work {
+ atomic_t bios_inflight;
+ struct sg_table sg_table;
+ struct scatterlist *src_sgl;
+};
+
+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
+{
+ struct sg_table *sgt = &bw->sg_table;
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *pg = sg_page(sg);
+ if (pg == NULL)
+ break;
+ __free_page(pg);
+ }
+
+ sg_free_table(sgt);
+ kfree(bw);
+ return;
+}
+
+static void blk_bio_map_kern_endio(struct bio *bio, int err)
+{
+ struct blk_kern_sg_work *bw = bio->bi_private;
+
+ if (bw != NULL) {
+ /* Decrement the bios in processing and, if zero, free */
+ BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
+ if (atomic_dec_and_test(&bw->bios_inflight)) {
+ if ((bio_data_dir(bio) == READ) && (err == 0)) {
+ unsigned long flags;
+
+ local_irq_save(flags); /* to protect KMs */
+ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
+ local_irq_restore(flags);
+ }
+ blk_free_kern_sg_work(bw);
+ }
+ }
+
+ bio_put(bio);
+ return;
+}
+
+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work **pbw,
+ gfp_t gfp, gfp_t page_gfp)
+{
+ int res = 0, i;
+ struct scatterlist *sg;
+ struct scatterlist *new_sgl;
+ int new_sgl_nents;
+ size_t len = 0, to_copy;
+ struct blk_kern_sg_work *bw;
+
+ bw = kzalloc(sizeof(*bw), gfp);
+ if (bw == NULL)
+ goto out;
+
+ bw->src_sgl = sgl;
+
+ for_each_sg(sgl, sg, nents, i)
+ len += sg->length;
+ to_copy = len;
+
+ new_sgl_nents = PFN_UP(len);
+
+ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
+ if (res != 0)
+ goto err_free;
+
+ new_sgl = bw->sg_table.sgl;
+
+ for_each_sg(new_sgl, sg, new_sgl_nents, i) {
+ struct page *pg;
+
+ pg = alloc_page(page_gfp);
+ if (pg == NULL)
+ goto err_free;
+
+ sg_assign_page(sg, pg);
+ sg->length = min_t(size_t, PAGE_SIZE, len);
+
+ len -= PAGE_SIZE;
+ }
+
+ if (rq_data_dir(rq) == WRITE) {
+ /*
+ * We need to limit amount of copied data to to_copy, because
+ * sgl might have the last element in sgl not marked as last in
+ * SG chaining.
+ */
+ sg_copy(new_sgl, sgl, 0, to_copy);
+ }
+
+ *pbw = bw;
+ /*
+ * REQ_COPY_USER name is misleading. It should be something like
+ * REQ_HAS_TAIL_SPACE_FOR_PADDING.
+ */
+ rq->cmd_flags |= REQ_COPY_USER;
+
+out:
+ return res;
+
+err_free:
+ blk_free_kern_sg_work(bw);
+ res = -ENOMEM;
+ goto out;
+}
+
+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
+{
+ int res;
+ struct request_queue *q = rq->q;
+ int rw = rq_data_dir(rq);
+ int max_nr_vecs, i;
+ size_t tot_len;
+ bool need_new_bio;
+ struct scatterlist *sg, *prev_sg = NULL;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int bios;
+
+ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
+ WARN_ON(1);
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Let's keep each bio allocation inside a single page to decrease
+ * probability of failure.
+ */
+ max_nr_vecs = min_t(size_t,
+ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
+ BIO_MAX_PAGES);
+
+ need_new_bio = true;
+ tot_len = 0;
+ bios = 0;
+ for_each_sg(sgl, sg, nents, i) {
+ struct page *page = sg_page(sg);
+ void *page_addr = page_address(page);
+ size_t len = sg->length, l;
+ size_t offset = sg->offset;
+
+ tot_len += len;
+ prev_sg = sg;
+
+ /*
+ * Each segment must be aligned on DMA boundary and
+ * not on stack. The last one may have unaligned
+ * length as long as the total length is aligned to
+ * DMA padding alignment.
+ */
+ if (i == nents - 1)
+ l = 0;
+ else
+ l = len;
+ if (((sg->offset | l) & queue_dma_alignment(q)) ||
+ (page_addr && object_is_on_stack(page_addr + sg->offset))) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ while (len > 0) {
+ size_t bytes;
+ int rc;
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(gfp, max_nr_vecs);
+ if (bio == NULL) {
+ res = -ENOMEM;
+ goto out_free_bios;
+ }
+
+ if (rw == WRITE)
+ bio->bi_rw |= REQ_WRITE;
+
+ bios++;
+ bio->bi_private = bw;
+ bio->bi_end_io = blk_bio_map_kern_endio;
+
+ if (hbio == NULL)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(size_t, len, PAGE_SIZE - offset);
+
+ rc = bio_add_pc_page(q, bio, page, bytes, offset);
+ if (rc < bytes) {
+ if (unlikely(need_new_bio || (rc < 0))) {
+ if (rc < 0)
+ res = rc;
+ else
+ res = -EIO;
+ goto out_free_bios;
+ } else {
+ need_new_bio = true;
+ len -= rc;
+ offset += rc;
+ continue;
+ }
+ }
+
+ need_new_bio = false;
+ offset = 0;
+ len -= bytes;
+ page = nth_page(page, 1);
+ }
+ }
+
+ if (hbio == NULL) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ /* Total length must be aligned on DMA padding alignment */
+ if ((tot_len & q->dma_pad_mask) &&
+ !(rq->cmd_flags & REQ_COPY_USER)) {
+ res = -EINVAL;
+ goto out_free_bios;
+ }
+
+ if (bw != NULL)
+ atomic_set(&bw->bios_inflight, bios);
+
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+
+ blk_queue_bounce(q, &bio);
+
+ res = blk_rq_append_bio(q, rq, bio);
+ if (unlikely(res != 0)) {
+ bio->bi_next = hbio;
+ hbio = bio;
+ /* We can have one or more bios bounced */
+ goto out_unmap_bios;
+ }
+ }
+
+ res = 0;
+
+ rq->buffer = NULL;
+out:
+ return res;
+
+out_unmap_bios:
+ blk_rq_unmap_kern_sg(rq, res);
+
+out_free_bios:
+ while (hbio != NULL) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ goto out;
+}
+
+/**
+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
+ * @rq: request to fill
+ * @sgl: area to map
+ * @nents: number of elements in @sgl
+ * @gfp: memory allocation flags
+ *
+ * Description:
+ * Data will be mapped directly if possible. Otherwise a bounce
+ * buffer will be used.
+ */
+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp)
+{
+ int res;
+
+ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
+ if (unlikely(res != 0)) {
+ struct blk_kern_sg_work *bw = NULL;
+
+ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
+ gfp, rq->q->bounce_gfp | gfp);
+ if (unlikely(res != 0))
+ goto out;
+
+ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
+ bw->sg_table.nents, bw, gfp);
+ if (res != 0) {
+ blk_free_kern_sg_work(bw);
+ goto out;
+ }
+ }
+
+ rq->buffer = NULL;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(blk_rq_map_kern_sg);
+
+/**
+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
+ * @rq: request to unmap
+ * @err: non-zero error code
+ *
+ * Description:
+ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
+ * only in case of an error!
+ */
+void blk_rq_unmap_kern_sg(struct request *rq, int err)
+{
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ struct bio *b = bio;
+ bio = bio->bi_next;
+ b->bi_end_io(b, err);
+ }
+ rq->bio = NULL;
+
+ return;
+}
+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
+
/**
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
* @q: request queue where request should be inserted
=== modified file 'include/linux/blkdev.h'
--- old/include/linux/blkdev.h 2013-05-11 05:39:14 +0000
+++ new/include/linux/blkdev.h 2013-05-11 05:48:04 +0000
@@ -670,6 +670,8 @@ extern unsigned long blk_max_low_pfn, bl
#define BLK_DEFAULT_SG_TIMEOUT (60 * HZ)
#define BLK_MIN_SG_TIMEOUT (7 * HZ)
+#define SCSI_EXEC_REQ_FIFO_DEFINED
+
#ifdef CONFIG_BOUNCE
extern int init_emergency_isa_pool(void);
extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
@@ -789,6 +791,9 @@ extern int blk_rq_map_kern(struct reques
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
struct rq_map_data *, struct sg_iovec *, int,
unsigned int, gfp_t);
+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
+ int nents, gfp_t gfp);
+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
struct request *, int);
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
=== modified file 'include/linux/scatterlist.h'
--- old/include/linux/scatterlist.h 2013-05-11 05:39:14 +0000
+++ new/include/linux/scatterlist.h 2013-05-11 05:48:04 +0000
@@ -8,6 +8,7 @@
#include <asm/types.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/kmap_types.h>
struct sg_table {
struct scatterlist *sgl; /* the list */
@@ -225,6 +226,9 @@ size_t sg_copy_from_buffer(struct scatte
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen);
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len);
+
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
=== modified file 'lib/scatterlist.c'
--- old/lib/scatterlist.c 2013-05-11 05:39:14 +0000
+++ new/lib/scatterlist.c 2013-05-14 01:25:01 +0000
@@ -629,3 +629,126 @@ size_t sg_copy_to_buffer(struct scatterl
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
}
EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/*
+ * Can switch to the next dst_sg element, so, to copy to strictly only
+ * one dst_sg element, it must be either last in the chain, or
+ * copy_len == dst_sg->length.
+ */
+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
+ size_t *pdst_offs, struct scatterlist *src_sg,
+ size_t copy_len)
+{
+ int res = 0;
+ struct scatterlist *dst_sg;
+ size_t src_len, dst_len, src_offs, dst_offs;
+ struct page *src_page, *dst_page;
+
+ dst_sg = *pdst_sg;
+ dst_len = *pdst_len;
+ dst_offs = *pdst_offs;
+ dst_page = sg_page(dst_sg);
+
+ src_page = sg_page(src_sg);
+ src_len = src_sg->length;
+ src_offs = src_sg->offset;
+
+ do {
+ void *saddr, *daddr;
+ size_t n;
+
+ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
+ (src_offs & ~PAGE_MASK);
+ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
+ (dst_offs & ~PAGE_MASK);
+
+ if (((src_offs & ~PAGE_MASK) == 0) &&
+ ((dst_offs & ~PAGE_MASK) == 0) &&
+ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
+ (copy_len >= PAGE_SIZE)) {
+ copy_page(daddr, saddr);
+ n = PAGE_SIZE;
+ } else {
+ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
+ PAGE_SIZE - (src_offs & ~PAGE_MASK));
+ n = min(n, src_len);
+ n = min(n, dst_len);
+ n = min_t(size_t, n, copy_len);
+ memcpy(daddr, saddr, n);
+ }
+ dst_offs += n;
+ src_offs += n;
+
+ kunmap_atomic(saddr);
+ kunmap_atomic(daddr);
+
+ res += n;
+ copy_len -= n;
+ if (copy_len == 0)
+ goto out;
+
+ src_len -= n;
+ dst_len -= n;
+ if (dst_len == 0) {
+ dst_sg = sg_next(dst_sg);
+ if (dst_sg == NULL)
+ goto out;
+ dst_page = sg_page(dst_sg);
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+ }
+ } while (src_len > 0);
+
+out:
+ *pdst_sg = dst_sg;
+ *pdst_len = dst_len;
+ *pdst_offs = dst_offs;
+ return res;
+}
+
+/**
+ * sg_copy - copy one SG vector to another
+ * @dst_sg: destination SG
+ * @src_sg: source SG
+ * @nents_to_copy: maximum number of entries to copy
+ * @copy_len: maximum amount of data to copy. If 0, then copy all.
+ *
+ * Description:
+ * Data from the source SG vector will be copied to the destination SG
+ * vector. End of the vectors will be determined by sg_next() returning
+ * NULL. Returns number of bytes copied.
+ */
+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
+ int nents_to_copy, size_t copy_len)
+{
+ int res = 0;
+ size_t dst_len, dst_offs;
+
+ if (copy_len == 0)
+ copy_len = 0x7FFFFFFF; /* copy all */
+
+ if (nents_to_copy == 0)
+ nents_to_copy = 0x7FFFFFFF; /* copy all */
+
+ dst_len = dst_sg->length;
+ dst_offs = dst_sg->offset;
+
+ do {
+ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
+ src_sg, copy_len);
+ copy_len -= copied;
+ res += copied;
+ if ((copy_len == 0) || (dst_sg == NULL))
+ goto out;
+
+ nents_to_copy--;
+ if (nents_to_copy == 0)
+ goto out;
+
+ src_sg = sg_next(src_sg);
+ } while (src_sg != NULL);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(sg_copy);
+6 -6
View File
@@ -47,7 +47,7 @@ static void disk_detach(struct scst_device *dev);
static int disk_parse(struct scst_cmd *cmd);
static int disk_perf_exec(struct scst_cmd *cmd);
static int disk_done(struct scst_cmd *cmd);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
static int disk_exec(struct scst_cmd *cmd);
static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd);
#endif
@@ -61,7 +61,7 @@ static struct scst_dev_type disk_devtype = {
.attach = disk_attach,
.detach = disk_detach,
.parse = disk_parse,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
.exec = disk_exec,
.on_sg_tablesize_low = disk_on_sg_tablesize_low,
#endif
@@ -82,7 +82,7 @@ static struct scst_dev_type disk_devtype_perf = {
.parse = disk_parse,
.exec = disk_perf_exec,
.dev_done = disk_done,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
.on_sg_tablesize_low = disk_on_sg_tablesize_low,
#endif
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
@@ -293,7 +293,7 @@ static int disk_done(struct scst_cmd *cmd)
return res;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd)
{
@@ -536,7 +536,7 @@ out:
return res;
out_err_restore:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out_restore;
out_error:
@@ -544,7 +544,7 @@ out_error:
goto out_done;
}
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
static int disk_perf_exec(struct scst_cmd *cmd)
{
+1 -1
View File
@@ -291,7 +291,7 @@ static int tape_done(struct scst_cmd *cmd)
PRINT_ERROR("Sense format 0x%x is not supported",
scst_sense_response_code(cmd->sense));
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
+16 -16
View File
@@ -1155,7 +1155,7 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
/*
* If we don't put such commands in the queue head, then under
* high load we might delay threads, waiting for memory
* allocations, for too long and start loosing NOPs, which
* allocations, for too long and start losing NOPs, which
* would lead to consider us by remote initiators as
* unresponsive and stuck => broken connections, etc. If none
* of our commands completed in NOP timeout to allow the head
@@ -1259,7 +1259,7 @@ out_unmap:
ucmd->data_pages = NULL;
res = -EFAULT;
if (ucmd->cmd != NULL)
scst_set_cmd_error(ucmd->cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
scst_set_cmd_error(ucmd->cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out_err;
}
@@ -1370,14 +1370,14 @@ out_process:
out_inval:
PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %s, cmd %p)",
(long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd);
(unsigned long long int)cmd->lun, scst_get_opcode_name(cmd), cmd);
PRINT_BUFFER("Invalid parse_reply", reply, sizeof(*reply));
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
res = -EINVAL;
goto out_abnormal;
out_hwerr_res_set:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
out_intern_fail_res_set:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
out_abnormal:
scst_set_cmd_abnormal_done_state(cmd);
@@ -1392,7 +1392,7 @@ out_status:
res = scst_alloc_sense(cmd, 0);
if (res != 0)
goto out_hwerr_res_set;
goto out_intern_fail_res_set;
sense_len = min_t(int, cmd->sense_buflen, preply->sense_len);
@@ -1402,7 +1402,7 @@ out_status:
if (rc != 0) {
PRINT_ERROR("Failed to copy %d sense's bytes", rc);
res = -EFAULT;
goto out_hwerr_res_set;
goto out_intern_fail_res_set;
}
cmd->sense_valid_len = sense_len;
}
@@ -1494,7 +1494,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
if (unlikely((ereply->pbuf & ~PAGE_MASK) != 0)) {
PRINT_ERROR("Supplied pbuf %llx isn't "
"page aligned", ereply->pbuf);
goto out_hwerr;
goto out_intern_fail;
}
pages = cmd->sg_cnt;
} else
@@ -1539,7 +1539,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
if (rc != 0) {
PRINT_ERROR("Failed to copy %d sense's bytes", rc);
res = -EFAULT;
goto out_hwerr_res_set;
goto out_intern_fail_res_set;
}
cmd->sense_valid_len = sense_len;
}
@@ -1556,19 +1556,19 @@ out:
out_inval:
PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %s, cmd %p)",
(long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd);
(unsigned long long int)cmd->lun, scst_get_opcode_name(cmd), cmd);
PRINT_BUFFER("Invalid exec_reply", reply, sizeof(*reply));
out_hwerr:
out_intern_fail:
res = -EINVAL;
out_hwerr_res_set:
out_intern_fail_res_set:
if (ucmd->background_exec) {
ucmd_put(ucmd);
goto out;
} else {
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out_compl;
}
@@ -1996,7 +1996,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
goto out_up;
}
TRACE_DBG("ureply %lld (dev %s)", (long long unsigned int)ureply,
TRACE_DBG("ureply %lld (dev %s)", (unsigned long long int)ureply,
dev->name);
cmd = kmem_cache_alloc(user_get_cmd_cachep, GFP_KERNEL);
@@ -2260,7 +2260,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
scst_set_busy(ucmd->cmd);
else
scst_set_cmd_error(ucmd->cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_lun_not_supported));
}
scst_set_cmd_abnormal_done_state(ucmd->cmd);
@@ -2291,7 +2291,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
scst_set_busy(ucmd->cmd);
else
scst_set_cmd_error(ucmd->cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_lun_not_supported));
}
ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT,
+85 -81
View File
@@ -184,7 +184,7 @@ struct scst_vdisk_dev {
uint64_t format_progress_to_do, format_progress_done;
int virt_id;
char name[16+1]; /* Name of the virtual device,
char name[64+1]; /* Name of the virtual device,
must be <= SCSI Model + 1 */
char *filename; /* File name, protected by
scst_mutex and suspended activities */
@@ -1387,8 +1387,8 @@ static int vdisk_attach(struct scst_device *dev)
(dev->type == TYPE_DISK) ? "disk" : "cdrom",
virt_dev->name, vdev_get_filename(virt_dev),
virt_dev->file_size >> 20, dev->block_size,
(long long unsigned int)virt_dev->nblocks,
(long long unsigned int)virt_dev->nblocks/64/32,
(unsigned long long int)virt_dev->nblocks,
(unsigned long long int)virt_dev->nblocks/64/32,
virt_dev->nblocks < 64*32
? " !WARNING! cyln less than 1" : "");
} else {
@@ -1541,8 +1541,8 @@ static enum compl_status_e vdisk_synchronize_cache(struct vdisk_cmd_params *p)
TRACE(TRACE_ORDER, "SYNCHRONIZE_CACHE: "
"loff=%lld, data_len=%lld, immed=%d",
(long long unsigned int)loff,
(long long unsigned int)data_len, immed);
(unsigned long long int)loff,
(unsigned long long int)data_len, immed);
if (data_len == 0) {
struct scst_vdisk_dev *virt_dev = dev->dh_priv;
@@ -2326,9 +2326,9 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
loff = (loff_t)lba_start << dev->block_shift;
TRACE_DBG("cmd %p, lba_start %lld, loff %lld, data_len %lld", cmd,
(long long unsigned int)lba_start,
(long long unsigned int)loff,
(long long unsigned int)data_len);
(unsigned long long int)lba_start,
(unsigned long long int)loff,
(unsigned long long int)data_len);
EXTRACHECKS_BUG_ON((loff < 0) || unlikely(data_len < 0));
@@ -2340,9 +2340,9 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
} else {
PRINT_INFO("Access beyond the end of device %s "
"(%lld of %lld, data len %lld)", virt_dev->name,
(long long unsigned int)loff,
(long long unsigned int)virt_dev->file_size,
(long long unsigned int)data_len);
(unsigned long long int)loff,
(unsigned long long int)virt_dev->file_size,
(unsigned long long int)data_len);
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
scst_sense_block_out_range_error));
}
@@ -2358,8 +2358,8 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
fua = (cdb[1] & 0x8);
if (fua) {
TRACE(TRACE_ORDER, "FUA: loff=%lld, "
"data_len=%lld", (long long unsigned int)loff,
(long long unsigned int)data_len);
"data_len=%lld", (unsigned long long int)loff,
(unsigned long long int)data_len);
}
break;
}
@@ -2777,7 +2777,8 @@ static int fileio_alloc_data_buf(struct scst_cmd *cmd)
* copy.
*/
if (cmd->tgt_i_data_buf_alloced ||
(cmd->data_direction & SCST_DATA_READ) == 0) {
(cmd->data_direction & SCST_DATA_READ) == 0 ||
(virt_dev->fd && !virt_dev->fd->f_mapping->a_ops->readpage)) {
p->use_zero_copy = false;
}
if (!p->use_zero_copy)
@@ -3119,7 +3120,7 @@ static int vdisk_unmap_range(struct scst_cmd *cmd,
#endif
} else {
loff_t off = start_lba << cmd->dev->block_shift;
loff_t len = blocks << cmd->dev->block_shift;
loff_t len = (u64)blocks << cmd->dev->block_shift;
res = vdisk_unmap_file_range(cmd, virt_dev, off, len, fd);
if (unlikely(res != 0))
@@ -3847,16 +3848,21 @@ static int vdisk_format_pg(unsigned char *p, int pcontrol,
static int vdisk_caching_pg(unsigned char *p, int pcontrol,
struct scst_vdisk_dev *virt_dev)
{ /* Caching page for mode_sense */
unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
{
/* Caching page for mode_sense */
static const unsigned char caching_pg[] = {
0x8, 0x12, 0x0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x80, 0x14, 0, 0,
0, 0, 0, 0
};
memcpy(p, caching_pg, sizeof(caching_pg));
if (!virt_dev->nv_cache && vdev_saved_mode_pages_enabled)
caching_pg[0] |= 0x80;
p[0] |= 0x80;
switch (pcontrol) {
case 0: /* current */
memcpy(p, caching_pg, sizeof(caching_pg));
p[2] |= (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : WCE;
break;
case 1: /* changeable */
@@ -3865,11 +3871,9 @@ static int vdisk_caching_pg(unsigned char *p, int pcontrol,
p[2] |= WCE;
break;
case 2: /* default */
memcpy(p, caching_pg, sizeof(caching_pg));
p[2] |= (DEF_WRITE_THROUGH || virt_dev->nv_cache) ? 0 : WCE;
break;
case 3: /* saved */
memcpy(p, caching_pg, sizeof(caching_pg));
p[2] |= (virt_dev->wt_flag_saved || virt_dev->nv_cache) ? 0 : WCE;
break;
default:
@@ -4923,7 +4927,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p)
if (unlikely(length < 0)) {
PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
@@ -4952,7 +4956,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p)
} else if (unlikely(length < 0)) {
PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out_set_fs;
}
@@ -4964,7 +4968,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p)
if ((err < 0) || (err < full_len)) {
PRINT_ERROR("readv() returned %lld from %zd",
(long long unsigned int)err,
(unsigned long long int)err,
full_len);
if (err == -EAGAIN)
scst_set_busy(cmd);
@@ -5046,7 +5050,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p)
if (unlikely(length < 0)) {
PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
@@ -5074,7 +5078,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p)
} else if (unlikely(length < 0)) {
PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out_set_fs;
}
@@ -5089,7 +5093,7 @@ restart:
if (err < 0) {
PRINT_ERROR("write() returned %lld from %zd",
(long long unsigned int)err,
(unsigned long long int)err,
full_len);
if (err == -EAGAIN)
scst_set_busy(cmd);
@@ -5319,15 +5323,18 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua)
/* Allocate and initialize blockio_work struct */
blockio_work = kmem_cache_alloc(blockio_work_cachep, gfp_mask);
if (blockio_work == NULL)
goto out_no_mem;
if (blockio_work == NULL) {
scst_set_busy(cmd);
goto finish_cmd;
}
#if 0
{
static int err_inj_cntr;
if (++err_inj_cntr % 256 == 0) {
PRINT_INFO("blockio_exec_rw() error injection");
goto out_no_bio;
scst_set_busy(cmd);
goto free_bio;
}
}
#endif
@@ -5345,6 +5352,18 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua)
need_new_bio = 1;
length = scst_get_sg_page_first(cmd, &page, &offset);
/*
* bv_len and bv_offset must be a multiple of 512 (SECTOR_SIZE), so
* check this here.
*/
if (WARN_ONCE((length & 511) != 0 || (offset & 511) != 0,
"Refused bio with invalid length %d and/or offset %d.\n",
length, offset)) {
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto free_bio;
}
while (length > 0) {
int len, bytes, off, thislen;
struct page *pg;
@@ -5370,7 +5389,8 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua)
PRINT_ERROR("Failed to create bio "
"for data segment %d (cmd %p)",
cmd->get_sg_buf_entry_num, cmd);
goto out_no_bio;
scst_set_busy(cmd);
goto free_bio;
}
bios++;
@@ -5457,7 +5477,7 @@ out:
TRACE_EXIT();
return;
out_no_bio:
free_bio:
while (hbio) {
bio = hbio;
hbio = hbio->bi_next;
@@ -5465,8 +5485,7 @@ out_no_bio:
}
kmem_cache_free(blockio_work_cachep, blockio_work);
out_no_mem:
scst_set_busy(cmd);
finish_cmd:
cmd->completed = 1;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
goto out;
@@ -5703,17 +5722,7 @@ static ssize_t fileio_read_sync(struct file *fd, void *buf, size_t len,
old_fs = get_fs();
set_fs(get_ds());
if (fd->f_op->llseek)
ret = fd->f_op->llseek(fd, *loff, 0/*SEEK_SET*/);
else
ret = default_llseek(fd, *loff, 0/*SEEK_SET*/);
if (ret < 0)
goto out;
ret = vfs_read(fd, (char __force __user *)buf, len, loff);
out:
set_fs(old_fs);
return ret;
@@ -5728,17 +5737,7 @@ static ssize_t fileio_write_sync(struct file *fd, void *buf, size_t len,
old_fs = get_fs();
set_fs(get_ds());
if (fd->f_op->llseek)
ret = fd->f_op->llseek(fd, *loff, 0/*SEEK_SET*/);
else
ret = default_llseek(fd, *loff, 0/*SEEK_SET*/);
if (ret < 0)
goto out;
ret = vfs_write(fd, (char __force __user *)buf, len, loff);
out:
set_fs(old_fs);
return ret;
@@ -5837,7 +5836,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p)
err = vdev_read_sync(virt_dev, mem_verify, len_mem, &loff);
if ((err < 0) || (err < len_mem)) {
PRINT_ERROR("verify() returned %lld from %zd",
(long long unsigned int)err, len_mem);
(unsigned long long int)err, len_mem);
if (err == -EAGAIN)
scst_set_busy(cmd);
else {
@@ -5868,7 +5867,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p)
if (length < 0) {
PRINT_ERROR("scst_get_buf_() failed: %zd", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
}
out_free:
@@ -5926,7 +5925,7 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p)
scst_set_busy(cmd);
else
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
@@ -6067,64 +6066,69 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd,
static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev)
{
char buf[128];
enum { buf_size = 256 };
char *buf = kmalloc(buf_size, GFP_KERNEL);
int i, j;
i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ",
if (!buf) {
PRINT_ERROR("%s: out of memory", __func__);
return;
}
i = snprintf(buf, buf_size, "Registering virtual %s device %s ",
virt_dev->vdev_devt->name, virt_dev->name);
j = i;
if (virt_dev->wt_flag)
i += snprintf(&buf[i], sizeof(buf) - i, "(WRITE_THROUGH");
i += snprintf(&buf[i], buf_size - i, "(WRITE_THROUGH");
if (virt_dev->nv_cache)
i += snprintf(&buf[i], sizeof(buf) - i, "%sNV_CACHE",
i += snprintf(&buf[i], buf_size - i, "%sNV_CACHE",
(j == i) ? "(" : ", ");
if (virt_dev->rd_only)
i += snprintf(&buf[i], sizeof(buf) - i, "%sREAD_ONLY",
i += snprintf(&buf[i], buf_size - i, "%sREAD_ONLY",
(j == i) ? "(" : ", ");
if (virt_dev->o_direct_flag)
i += snprintf(&buf[i], sizeof(buf) - i, "%sO_DIRECT",
i += snprintf(&buf[i], buf_size - i, "%sO_DIRECT",
(j == i) ? "(" : ", ");
if (virt_dev->nullio)
i += snprintf(&buf[i], sizeof(buf) - i, "%sNULLIO",
i += snprintf(&buf[i], buf_size - i, "%sNULLIO",
(j == i) ? "(" : ", ");
if (virt_dev->blockio)
i += snprintf(&buf[i], sizeof(buf) - i, "%sBLOCKIO",
i += snprintf(&buf[i], buf_size - i, "%sBLOCKIO",
(j == i) ? "(" : ", ");
if (virt_dev->removable)
i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE",
i += snprintf(&buf[i], buf_size - i, "%sREMOVABLE",
(j == i) ? "(" : ", ");
if (virt_dev->tst != DEF_TST)
i += snprintf(&buf[i], sizeof(buf) - i, "%sTST %d",
i += snprintf(&buf[i], buf_size - i, "%sTST %d",
(j == i) ? "(" : ", ", virt_dev->tst);
if (virt_dev->rotational)
i += snprintf(&buf[i], sizeof(buf) - i, "%sROTATIONAL",
i += snprintf(&buf[i], buf_size - i, "%sROTATIONAL",
(j == i) ? "(" : ", ");
if (virt_dev->thin_provisioned)
i += snprintf(&buf[i], sizeof(buf) - i, "%sTHIN_PROVISIONED",
i += snprintf(&buf[i], buf_size - i, "%sTHIN_PROVISIONED",
(j == i) ? "(" : ", ");
if (virt_dev->zero_copy)
i += snprintf(&buf[i], sizeof(buf) - i, "%sZERO_COPY",
i += snprintf(&buf[i], buf_size - i, "%sZERO_COPY",
(j == i) ? "(" : ", ");
if (virt_dev->dummy)
i += snprintf(&buf[i], sizeof(buf) - i, "%sDUMMY",
i += snprintf(&buf[i], buf_size - i, "%sDUMMY",
(j == i) ? "(" : ", ");
if (j == i)
PRINT_INFO("%s", buf);
else
PRINT_INFO("%s)", buf);
PRINT_INFO("%s%s", buf, j == i ? "" : ")");
kfree(buf);
return;
}
@@ -6159,8 +6163,8 @@ static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev)
"(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)",
virt_dev->name, virt_dev->file_size >> 20,
virt_dev->dev->block_size,
(long long unsigned int)virt_dev->nblocks,
(long long unsigned int)virt_dev->nblocks/64/32,
(unsigned long long int)virt_dev->nblocks,
(unsigned long long int)virt_dev->nblocks/64/32,
virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
"than 1" : "");
@@ -6959,8 +6963,8 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
" cyln=%lld%s)", virt_dev->name,
vdev_get_filename(virt_dev),
virt_dev->file_size >> 20, virt_dev->dev->block_size,
(long long unsigned int)virt_dev->nblocks,
(long long unsigned int)virt_dev->nblocks/64/32,
(unsigned long long int)virt_dev->nblocks,
(unsigned long long int)virt_dev->nblocks/64/32,
virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
"than 1" : "");
} else {
@@ -8033,7 +8037,7 @@ static ssize_t vdev_sysfs_naa_id_store(struct kobject *kobj,
switch (c) {
case 0:
case 2 * 8:
if (strchr("1235cCdDeEfF", buf[0]))
if (strchr("235", buf[0]))
break;
else
goto out;
+434 -75
View File
@@ -109,17 +109,18 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
*/
int hex_to_bin(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
ch = tolower(ch);
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
return -1;
if (ch >= '0' && ch <= '9')
return ch - '0';
ch = tolower(ch);
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
return -1;
}
EXPORT_SYMBOL(hex_to_bin);
#endif
#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) && !defined(HAVE_SG_COPY)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) || \
!defined(SCSI_EXEC_REQ_FIFO_DEFINED)
static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
int nents_to_copy, size_t copy_len,
@@ -554,17 +555,17 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
{.ops = 0x04, .devkey = "M O O ",
.info_op_name = "FORMAT UNIT",
.info_data_direction = SCST_DATA_NONE,
.info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
.info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM|SCST_STRICTLY_SERIALIZED,
.get_cdb_info = get_cdb_info_fmt},
{.ops = 0x04, .devkey = " O ",
.info_op_name = "FORMAT MEDIUM",
.info_data_direction = SCST_DATA_WRITE,
.info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
.info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM|SCST_STRICTLY_SERIALIZED,
.info_len_off = 3, .get_cdb_info = get_cdb_info_len_2},
{.ops = 0x04, .devkey = " O ",
.info_op_name = "FORMAT",
.info_data_direction = SCST_DATA_NONE,
.info_op_flags = SCST_WRITE_MEDIUM,
.info_op_flags = SCST_WRITE_MEDIUM|SCST_STRICTLY_SERIALIZED,
.get_cdb_info = get_cdb_info_none},
{.ops = 0x05, .devkey = "VMVVVV V ",
.info_op_name = "READ BLOCK LIMITS",
@@ -952,15 +953,15 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
.info_op_name = "WRITE BUFFER",
.info_data_direction = SCST_DATA_WRITE,
.info_op_flags = SCST_SMALL_TIMEOUT,
.info_len_off = 5, .info_len_len = 2,
.get_cdb_info = get_cdb_info_len_2},
.info_len_off = 6, .info_len_len = 3,
.get_cdb_info = get_cdb_info_len_3},
{.ops = 0x3C, .devkey = "OOOOOOOOOOOOOOOO",
.info_op_name = "READ BUFFER",
.info_data_direction = SCST_DATA_READ,
.info_op_flags = SCST_SMALL_TIMEOUT |
SCST_WRITE_EXCL_ALLOWED,
.info_len_off = 5, .info_len_len = 2,
.get_cdb_info = get_cdb_info_len_2},
.info_len_off = 6, .info_len_len = 3,
.get_cdb_info = get_cdb_info_len_3},
{.ops = 0x3D, .devkey = " O O ",
.info_op_name = "UPDATE BLOCK",
.info_data_direction = SCST_DATA_WRITE,
@@ -3176,7 +3177,7 @@ static bool __scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg,
TRACE_DBG_FLAG(TRACE_SG_OP|TRACE_MEMORY|TRACE_DEBUG,
"cmd %p (tag %llu), sg %p, sg_cnt %d, "
"adjust_len %d, i %d, sg[j].length %d, left %d",
cmd, (long long unsigned int)cmd->tag,
cmd, (unsigned long long int)cmd->tag,
sg, *sg_cnt, adjust_len, i,
sgi->length, left);
@@ -4642,7 +4643,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
scst_sgv_pool_use_dma(tgt_dev);
TRACE_MGMT_DBG("Device %s on SCST lun=%lld",
dev->virt_name, (long long unsigned int)tgt_dev->lun);
dev->virt_name, (unsigned long long int)tgt_dev->lun);
spin_lock_init(&tgt_dev->tgt_dev_lock);
INIT_LIST_HEAD(&tgt_dev->UA_list);
@@ -5128,7 +5129,7 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd)
PRINT_ERROR("%s", "Unable to get the sense via "
"REQUEST SENSE, returning HARDWARE ERROR");
scst_set_cmd_error(orig_cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
}
}
@@ -5917,7 +5918,7 @@ void scst_free_cmd(struct scst_cmd *cmd)
TRACE_ENTRY();
TRACE_DBG("Freeing cmd %p (tag %llu)",
cmd, (long long unsigned int)cmd->tag);
cmd, (unsigned long long int)cmd->tag);
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
TRACE_MGMT_DBG("Freeing aborted cmd %p", cmd);
@@ -5958,7 +5959,7 @@ void scst_free_cmd(struct scst_cmd *cmd)
&cmd->cmd_flags);
TRACE_SN("Out of SN cmd %p (tag %llu, sn %d), "
"destroy=%d", cmd,
(long long unsigned int)cmd->tag,
(unsigned long long int)cmd->tag,
cmd->sn, destroy);
}
}
@@ -6221,8 +6222,373 @@ out:
return;
}
#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) && !defined(HAVE_SG_COPY)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
struct blk_kern_sg_work {
atomic_t bios_inflight;
struct sg_table sg_table;
struct scatterlist *src_sgl;
};
static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
{
struct sg_table *sgt = &bw->sg_table;
struct scatterlist *sg;
struct page *pg;
int i;
for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
pg = sg_page(sg);
if (pg == NULL)
break;
__free_page(pg);
}
sg_free_table(sgt);
kfree(bw);
return;
}
static void blk_bio_map_kern_endio(struct bio *bio, int err)
{
struct blk_kern_sg_work *bw = bio->bi_private;
if (bw != NULL) {
/* Decrement the bios in processing and, if zero, free */
BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
if (atomic_dec_and_test(&bw->bios_inflight)) {
if (bio_data_dir(bio) == READ && err == 0) {
unsigned long flags;
local_irq_save(flags); /* to protect KMs */
sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
, KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ
#endif
);
local_irq_restore(flags);
}
blk_free_kern_sg_work(bw);
}
}
bio_put(bio);
return;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
/*
* See also patch "block: Add blk_make_request(), takes bio, returns a
* request" (commit 79eb63e9e5875b84341a3a05f8e6ae9cdb4bb6f6).
*/
static struct request *blk_make_request(struct request_queue *q,
struct bio *bio,
gfp_t gfp_mask)
{
struct request *rq = blk_get_request(q, bio_data_dir(bio), gfp_mask);
if (unlikely(!rq))
return ERR_PTR(-ENOMEM);
rq->cmd_type = REQ_TYPE_BLOCK_PC;
for ( ; bio; bio = bio->bi_next) {
struct bio *bounce_bio = bio;
int ret;
blk_queue_bounce(q, &bounce_bio);
ret = blk_rq_append_bio(q, rq, bounce_bio);
if (unlikely(ret)) {
blk_put_request(rq);
return ERR_PTR(ret);
}
}
return rq;
}
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
/*
* Copy an sg-list. This function is related to bio_copy_kern() but duplicates
* an sg-list instead of creating a bio out of a single kernel address range.
*/
static struct blk_kern_sg_work *blk_copy_kern_sg(struct request_queue *q,
struct scatterlist *sgl, int nents, gfp_t gfp_mask, bool reading)
{
int res = 0, i;
struct scatterlist *sg;
struct scatterlist *new_sgl;
int new_sgl_nents;
size_t len = 0, to_copy;
struct blk_kern_sg_work *bw;
res = -ENOMEM;
bw = kzalloc(sizeof(*bw), gfp_mask);
if (bw == NULL)
goto err;
bw->src_sgl = sgl;
for_each_sg(sgl, sg, nents, i)
len += sg->length;
to_copy = len;
new_sgl_nents = PFN_UP(len);
res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp_mask);
if (res != 0)
goto err_free_bw;
new_sgl = bw->sg_table.sgl;
res = -ENOMEM;
for_each_sg(new_sgl, sg, new_sgl_nents, i) {
struct page *pg;
pg = alloc_page(q->bounce_gfp | gfp_mask);
if (pg == NULL)
goto err_free_table;
sg_assign_page(sg, pg);
sg->length = min_t(size_t, PAGE_SIZE, len);
len -= PAGE_SIZE;
}
if (!reading) {
/*
* We need to limit amount of copied data to to_copy, because
* sgl might have the last element in sgl not marked as last in
* SG chaining.
*/
sg_copy(new_sgl, sgl, 0, to_copy
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
, KM_USER0, KM_USER1
#endif
);
}
out:
return bw;
err_free_table:
sg_free_table(&bw->sg_table);
err_free_bw:
blk_free_kern_sg_work(bw);
err:
sBUG_ON(res == 0);
bw = ERR_PTR(res);
goto out;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
static void bio_kmalloc_destructor(struct bio *bio)
{
kfree(bio->bi_io_vec);
kfree(bio);
}
#endif
/* __blk_map_kern_sg - map kernel data to a request for REQ_TYPE_BLOCK_PC */
static struct request *__blk_map_kern_sg(struct request_queue *q,
struct scatterlist *sgl, int nents, struct blk_kern_sg_work *bw,
gfp_t gfp_mask, bool reading)
{
struct request *rq;
int max_nr_vecs, i;
size_t tot_len;
bool need_new_bio;
struct scatterlist *sg;
struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
int bios;
if (unlikely(sgl == NULL || sgl->length == 0 || nents <= 0)) {
WARN_ON_ONCE(true);
rq = ERR_PTR(-EINVAL);
goto out;
}
/*
* Restrict bio size to a single page to minimize the probability that
* bio allocation fails.
*/
max_nr_vecs = min_t(int,
(PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),
BIO_MAX_PAGES);
need_new_bio = true;
tot_len = 0;
bios = 0;
for_each_sg(sgl, sg, nents, i) {
struct page *page = sg_page(sg);
void *page_addr = page_address(page);
size_t len = sg->length, l;
size_t offset = sg->offset;
tot_len += len;
/*
* Each segment must be DMA-aligned and must not reside not on
* the stack. The last segment may have unaligned length as
* long as the total length satisfies the DMA padding
* alignment requirements.
*/
if (i == nents - 1)
l = 0;
else
l = len;
if (((sg->offset | l) & queue_dma_alignment(q)) ||
(page_addr && object_is_on_stack(page_addr + sg->offset))) {
rq = ERR_PTR(-EINVAL);
goto out_free_bios;
}
while (len > 0) {
size_t bytes;
int rc;
if (need_new_bio) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
bio = bio_alloc_bioset(gfp_mask, max_nr_vecs, NULL);
if (bio)
bio->bi_destructor =
bio_kmalloc_destructor;
#else
bio = bio_kmalloc(gfp_mask, max_nr_vecs);
#endif
if (bio == NULL) {
rq = ERR_PTR(-ENOMEM);
goto out_free_bios;
}
if (!reading)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
bio->bi_rw |= 1 << BIO_RW;
#else
bio->bi_rw |= REQ_WRITE;
#endif
bios++;
bio->bi_private = bw;
bio->bi_end_io = blk_bio_map_kern_endio;
if (hbio == NULL)
hbio = bio;
else
tbio->bi_next = bio;
tbio = bio;
}
bytes = min_t(size_t, len, PAGE_SIZE - offset);
rc = bio_add_pc_page(q, bio, page, bytes, offset);
if (rc < bytes) {
if (unlikely(need_new_bio || rc < 0)) {
rq = ERR_PTR(rc < 0 ? rc : -EIO);
goto out_free_bios;
} else {
need_new_bio = true;
len -= rc;
offset += rc;
}
} else {
need_new_bio = false;
offset = 0;
len -= bytes;
page = nth_page(page, 1);
}
}
}
if (hbio == NULL) {
rq = ERR_PTR(-EINVAL);
goto out_free_bios;
}
/* Total length must satisfy DMA padding alignment */
if ((tot_len & q->dma_pad_mask) && bw != NULL) {
rq = ERR_PTR(-EINVAL);
goto out_free_bios;
}
rq = blk_make_request(q, hbio, gfp_mask);
if (unlikely(IS_ERR(rq)))
goto out_free_bios;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
/*
* See also patch "block: add blk_rq_set_block_pc()" (commit
* f27b087b81b7).
*/
rq->cmd_type = REQ_TYPE_BLOCK_PC;
#endif
if (bw != NULL) {
atomic_set(&bw->bios_inflight, bios);
rq->cmd_flags |= REQ_COPY_USER;
}
out:
return rq;
out_free_bios:
while (hbio != NULL) {
bio = hbio;
hbio = hbio->bi_next;
bio_put(bio);
}
goto out;
}
/**
* blk_map_kern_sg - map kernel data to a request for REQ_TYPE_BLOCK_PC
* @rq: request to fill
* @sgl: area to map
* @nents: number of elements in @sgl
* @gfp: memory allocation flags
*
* Description:
* Data will be mapped directly if possible. Otherwise a bounce
* buffer will be used.
*/
static struct request *blk_map_kern_sg(struct request_queue *q,
struct scatterlist *sgl, int nents, gfp_t gfp, bool reading)
{
struct request *rq;
if (!sgl) {
rq = blk_get_request(q, reading ? READ : WRITE, gfp);
if (unlikely(!rq))
return ERR_PTR(-ENOMEM);
rq->cmd_type = REQ_TYPE_BLOCK_PC;
goto out;
}
rq = __blk_map_kern_sg(q, sgl, nents, NULL, gfp, reading);
if (unlikely(IS_ERR(rq))) {
struct blk_kern_sg_work *bw;
bw = blk_copy_kern_sg(q, sgl, nents, gfp, reading);
if (unlikely(IS_ERR(bw))) {
rq = ERR_PTR(PTR_ERR(bw));
goto out;
}
rq = __blk_map_kern_sg(q, bw->sg_table.sgl, bw->sg_table.nents,
bw, gfp, reading);
if (IS_ERR(rq)) {
blk_free_kern_sg_work(bw);
goto out;
}
}
out:
return rq;
}
#endif
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
/*
* Can switch to the next dst_sg element, so, to copy to strictly only
* one dst_sg element, it must be either last in the chain, or
@@ -6374,16 +6740,22 @@ static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
out:
return res;
}
#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
#endif /* !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
static void scsi_end_async(struct request *req, int error)
{
struct scsi_io_context *sioc = req->end_io_data;
TRACE_DBG("sioc %p, cmd %p", sioc, sioc->data);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
lockdep_assert_held(req->q->queue_lock);
#else
if (!req->q->mq_ops)
lockdep_assert_held(req->q->queue_lock);
#endif
if (sioc->done)
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
sioc->done(sioc->data, sioc->sense, req->errors, req->data_len);
@@ -6410,7 +6782,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
struct request_queue *q = cmd->dev->scsi_dev->request_queue;
struct request *rq;
struct scsi_io_context *sioc;
int write = (cmd->data_direction & SCST_DATA_WRITE) ? WRITE : READ;
bool reading = !(cmd->data_direction & SCST_DATA_WRITE);
gfp_t gfp = cmd->cmd_gfp_mask;
int cmd_len = cmd->cdb_len;
@@ -6420,54 +6792,38 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
goto out;
}
rq = blk_get_request(q, write, gfp);
if (rq == NULL) {
res = -ENOMEM;
goto out_free_sioc;
}
rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->cmd_flags |= REQ_QUIET;
if (cmd->sg == NULL)
goto done;
if (cmd->data_direction == SCST_DATA_BIDI) {
struct request *next_rq;
if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) {
res = -EOPNOTSUPP;
goto out_free_rq;
goto out;
}
res = blk_rq_map_kern_sg(rq, cmd->out_sg, cmd->out_sg_cnt, gfp);
if (res != 0) {
TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res);
goto out_free_rq;
rq = blk_map_kern_sg(q, cmd->out_sg, cmd->out_sg_cnt, gfp,
reading);
if (IS_ERR(rq)) {
res = PTR_ERR(rq);
TRACE_DBG("blk_map_kern_sg() failed: %d", res);
goto out;
}
next_rq = blk_get_request(q, READ, gfp);
if (next_rq == NULL) {
res = -ENOMEM;
next_rq = blk_map_kern_sg(q, cmd->sg, cmd->sg_cnt, gfp, false);
if (IS_ERR(next_rq)) {
res = PTR_ERR(next_rq);
TRACE_DBG("blk_map_kern_sg() failed: %d", res);
goto out_free_unmap;
}
rq->next_rq = next_rq;
next_rq->cmd_type = rq->cmd_type;
res = blk_rq_map_kern_sg(next_rq, cmd->sg, cmd->sg_cnt, gfp);
if (res != 0) {
TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res);
goto out_free_unmap;
}
} else {
res = blk_rq_map_kern_sg(rq, cmd->sg, cmd->sg_cnt, gfp);
if (res != 0) {
TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res);
goto out_free_rq;
rq = blk_map_kern_sg(q, cmd->sg, cmd->sg_cnt, gfp, reading);
if (IS_ERR(rq)) {
res = PTR_ERR(rq);
TRACE_DBG("blk_map_kern_sg() failed: %d", res);
goto out;
}
}
done:
TRACE_DBG("sioc %p, cmd %p", sioc, cmd);
sioc->data = data;
@@ -6485,6 +6841,7 @@ done:
rq->timeout = cmd->timeout;
rq->retries = cmd->retries;
rq->end_io_data = sioc;
rq->cmd_flags |= REQ_QUIET;
blk_execute_rq_nowait(rq->q, NULL, rq,
(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE), scsi_end_async);
@@ -6492,22 +6849,23 @@ out:
return res;
out_free_unmap:
if (rq->next_rq != NULL) {
blk_put_request(rq->next_rq);
rq->next_rq = NULL;
{
struct bio *bio = rq->bio, *b;
while (bio) {
b = bio;
bio = bio->bi_next;
b->bi_end_io(b, res);
}
blk_rq_unmap_kern_sg(rq, res);
}
rq->bio = NULL;
out_free_rq:
blk_put_request(rq);
out_free_sioc:
kmem_cache_free(scsi_io_context_cache, sioc);
goto out;
}
EXPORT_SYMBOL(scst_scsi_exec_async);
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
/**
* scst_copy_sg() - copy data between the command's SGs
@@ -6657,7 +7015,7 @@ int scst_get_buf_full_sense(struct scst_cmd *cmd, uint8_t **buf)
scst_set_busy(cmd);
else
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
@@ -7523,7 +7881,8 @@ int scst_calc_block_shift(int sector_size)
sector_size = 512;
block_shift = ilog2(sector_size);
WARN_ON(1 << block_shift != sector_size);
WARN_ONCE(1 << block_shift != sector_size, "1 << %d != %d\n",
block_shift, sector_size);
if (block_shift < 9) {
PRINT_ERROR("Wrong sector size %d", sector_size);
@@ -8600,7 +8959,7 @@ static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev)
list_for_each_entry_safe(UA_entry, t,
&tgt_dev->UA_list, UA_list_entry) {
TRACE_MGMT_DBG("Clearing UA for tgt_dev LUN %lld",
(long long unsigned int)tgt_dev->lun);
(unsigned long long int)tgt_dev->lun);
list_del(&UA_entry->UA_list_entry);
mempool_free(UA_entry, scst_ua_mempool);
}
@@ -8683,7 +9042,7 @@ restart:
* !! destroyed! !!
*/
TRACE_SN("cmd %p (tag %llu) with skipped sn %d found",
cmd, (long long unsigned int)cmd->tag, cmd->sn);
cmd, (unsigned long long int)cmd->tag, cmd->sn);
order_data->def_cmd_count--;
list_del(&cmd->deferred_cmd_list_entry);
spin_unlock_irq(&order_data->sn_lock);
@@ -8783,13 +9142,13 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd)
if (dev->block_count > 0) {
TRACE_BLOCK("Delaying cmd %p due to blocking "
"(tag %llu, op %s, dev %s)", cmd,
(long long unsigned int)cmd->tag,
(unsigned long long int)cmd->tag,
scst_get_opcode_name(cmd), dev->virt_name);
goto out_block;
} else if ((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED) {
TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further "
"cmds on dev %s due to strict serialization", cmd,
(long long unsigned int)cmd->tag,
(unsigned long long int)cmd->tag,
scst_get_opcode_name(cmd), dev->virt_name);
scst_block_dev(dev);
if (dev->on_dev_cmd_count > 1) {
@@ -8804,7 +9163,7 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd)
} else if ((dev->dev_double_ua_possible) ||
((cmd->op_flags & SCST_SERIALIZED) != 0)) {
TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further cmds "
"on dev %s due to %s", cmd, (long long unsigned int)cmd->tag,
"on dev %s due to %s", cmd, (unsigned long long int)cmd->tag,
scst_get_opcode_name(cmd), dev->virt_name,
dev->dev_double_ua_possible ? "possible double reset UA" :
"serialized cmd");
@@ -9253,13 +9612,13 @@ void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd)
if (test_bit(SCST_CMD_DEVICE_TAS, &cmd->cmd_flags)) {
TRACE_MGMT_DBG("Flag ABORTED OTHER set for cmd %p "
"(tag %llu), returning TASK ABORTED ", cmd,
(long long unsigned int)cmd->tag);
(unsigned long long int)cmd->tag);
scst_set_cmd_error_status(cmd, SAM_STAT_TASK_ABORTED);
} else {
TRACE_MGMT_DBG("Flag ABORTED OTHER set for cmd %p "
"(tag %llu), aborting without delivery or "
"notification",
cmd, (long long unsigned int)cmd->tag);
cmd, (unsigned long long int)cmd->tag);
/*
* There is no need to check/requeue possible UA,
* because, if it exists, it will be delivered
@@ -10680,7 +11039,7 @@ void tm_dbg_release_cmd(struct scst_cmd *cmd)
if (((scst_random() % 10) == 5)) {
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(
scst_sense_hardw_error));
scst_sense_internal_failure));
/* It's completed now */
}
}
+46 -55
View File
@@ -47,18 +47,13 @@ option or use a 64-bit configuration instead. See README file for \
details.
#endif
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) && \
!defined(SCSI_EXEC_REQ_FIFO_DEFINED) && \
!defined(CONFIG_SCST_STRICT_SERIALIZING)
#warning Patch scst_exec_req_fifo-<kernel-version> was not applied on \
your kernel and CONFIG_SCST_STRICT_SERIALIZING is not defined. \
Pass-through dev handlers will not work.
#endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
#warning Patch scst_exec_req_fifo-<kernel-version> was not applied on \
your kernel. Pass-through dev handlers will not work.
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
#endif
/**
** SCST global variables. They are all uninitialized to have their layout in
@@ -1607,26 +1602,18 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type,
if (res != 0)
goto out;
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) && \
!defined(SCSI_EXEC_REQ_FIFO_DEFINED) && \
!defined(CONFIG_SCST_STRICT_SERIALIZING)
if (dev_type->exec == NULL) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
"supported. Consider applying on your kernel patch "
"scst_exec_req_fifo-<kernel-version> or define "
"CONFIG_SCST_STRICT_SERIALIZING", dev_type->name);
res = -EINVAL;
goto out;
#endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
"supported. Consider applying on your kernel patch "
"scst_exec_req_fifo-<kernel-version>", dev_type->name);
res = -EINVAL;
goto out;
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
}
#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
#endif
#ifdef CONFIG_SCST_PROC
res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER);
@@ -2513,55 +2500,59 @@ static int __init init_scst(void)
}
/* Used for rarely used or read-mostly on fast path structures */
#define INIT_CACHEP(p, s, o) do { \
p = KMEM_CACHE(s, SCST_SLAB_FLAGS); \
TRACE_MEM("Slab create: %s at %p size %zd", #s, p, \
#define INIT_CACHEP(p, s) ({ \
(p) = KMEM_CACHE(s, SCST_SLAB_FLAGS); \
TRACE_MEM("Slab create: %s at %p size %zd", #s, (p), \
sizeof(struct s)); \
if (p == NULL) { \
res = -ENOMEM; \
goto o; \
} \
} while (0)
(p); \
})
/* Used for structures with fast path write access */
#define INIT_CACHEP_ALIGN(p, s, o) do { \
p = KMEM_CACHE(s, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN); \
TRACE_MEM("Slab create: %s at %p size %zd", #s, p, \
#define INIT_CACHEP_ALIGN(p, s) ({ \
(p) = KMEM_CACHE(s, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);\
TRACE_MEM("Slab create: %s at %p size %zd", #s, (p), \
sizeof(struct s)); \
if (p == NULL) { \
res = -ENOMEM; \
goto o; \
} \
} while (0)
(p); \
})
INIT_CACHEP(scst_mgmt_cachep, scst_mgmt_cmd, out_lib_exit);
INIT_CACHEP(scst_mgmt_stub_cachep, scst_mgmt_cmd_stub,
out_destroy_mgmt_cache);
INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA,
out_destroy_mgmt_stub_cache);
res = -ENOMEM;
if (!INIT_CACHEP(scst_mgmt_cachep, scst_mgmt_cmd))
goto out_lib_exit;
if (!INIT_CACHEP(scst_mgmt_stub_cachep, scst_mgmt_cmd_stub))
goto out_destroy_mgmt_cache;
if (!INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA))
goto out_destroy_mgmt_stub_cache;
{
struct scst_sense { uint8_t s[SCST_SENSE_BUFFERSIZE]; };
INIT_CACHEP(scst_sense_cachep, scst_sense,
out_destroy_ua_cache);
if (!INIT_CACHEP(scst_sense_cachep, scst_sense))
goto out_destroy_ua_cache;
}
INIT_CACHEP(scst_aen_cachep, scst_aen, out_destroy_sense_cache); /* read-mostly */
INIT_CACHEP_ALIGN(scst_cmd_cachep, scst_cmd, out_destroy_aen_cache);
if (!INIT_CACHEP(scst_aen_cachep, scst_aen)) /* read-mostly */
goto out_destroy_sense_cache;
if (!INIT_CACHEP_ALIGN(scst_cmd_cachep, scst_cmd))
goto out_destroy_aen_cache;
#ifdef CONFIG_SCST_MEASURE_LATENCY
INIT_CACHEP_ALIGN(scst_sess_cachep, scst_session,
out_destroy_cmd_cache);
if (!INIT_CACHEP_ALIGN(scst_sess_cachep, scst_session))
goto out_destroy_cmd_cache;
#else
/* Big enough with read-mostly head and tail */
INIT_CACHEP(scst_sess_cachep, scst_session, out_destroy_cmd_cache);
if (!INIT_CACHEP(scst_sess_cachep, scst_session))
goto out_destroy_cmd_cache;
#endif
INIT_CACHEP(scst_dev_cachep, scst_device, out_destroy_sess_cache); /* big enough */
INIT_CACHEP(scst_tgt_cachep, scst_tgt, out_destroy_dev_cache); /* read-mostly */
if (!INIT_CACHEP(scst_dev_cachep, scst_device)) /* big enough */
goto out_destroy_sess_cache;
if (!INIT_CACHEP(scst_tgt_cachep, scst_tgt)) /* read-mostly */
goto out_destroy_dev_cache;
#ifdef CONFIG_SCST_MEASURE_LATENCY
INIT_CACHEP_ALIGN(scst_tgtd_cachep, scst_tgt_dev, out_destroy_tgt_cache); /* big enough */
if (!INIT_CACHEP_ALIGN(scst_tgtd_cachep, scst_tgt_dev)) /* big enough */
goto out_destroy_tgt_cache;
#else
/* Big enough with read-mostly head and tail */
INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev, out_destroy_tgt_cache); /* big enough */
if (!INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev)) /* big enough */
goto out_destroy_tgt_cache;
#endif
INIT_CACHEP(scst_acgd_cachep, scst_acg_dev, out_destroy_tgtd_cache); /* read-mostly */
if (!INIT_CACHEP(scst_acgd_cachep, scst_acg_dev)) /* read-mostly */
goto out_destroy_tgtd_cache;
scst_mgmt_mempool = mempool_create(64, mempool_alloc_slab,
mempool_free_slab, scst_mgmt_cachep);
@@ -2571,7 +2562,7 @@ static int __init init_scst(void)
}
/*
* All mgmt stubs, UAs and sense buffers are bursty and loosing them
* All mgmt stubs, UAs and sense buffers are bursty and losing them
* may have fatal consequences, so let's have big pools for them.
*/
+5 -5
View File
@@ -350,7 +350,7 @@ out_unlock_put:
goto out;
}
static unsigned long __sgv_can_be_shrinked(void)
static unsigned long __sgv_can_be_shrunk(void)
{
unsigned long res;
struct sgv_pool *pool;
@@ -374,10 +374,10 @@ static unsigned long __sgv_can_be_shrinked(void)
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
static unsigned long sgv_can_be_shrinked(struct shrinker *shrinker,
static unsigned long sgv_can_be_shrunk(struct shrinker *shrinker,
struct shrink_control *sc)
{
return __sgv_can_be_shrinked();
return __sgv_can_be_shrunk();
}
static unsigned long sgv_scan_shrink(struct shrinker *shrinker,
@@ -413,7 +413,7 @@ static int sgv_shrink(struct shrinker *shrinker, struct shrink_control *sc)
nr = __sgv_shrink(nr, SGV_MIN_SHRINK_INTERVAL, &freed);
TRACE_MEM("Left %d", nr);
} else
nr = __sgv_can_be_shrinked();
nr = __sgv_can_be_shrunk();
TRACE_EXIT_RES(nr);
return nr;
@@ -1809,7 +1809,7 @@ int scst_sgv_pools_init(unsigned long mem_hwmark, unsigned long mem_lwmark)
sgv_shrinker = set_shrinker(DEFAULT_SEEKS, sgv_shrink);
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
sgv_shrinker.count_objects = sgv_can_be_shrinked;
sgv_shrinker.count_objects = sgv_can_be_shrunk;
sgv_shrinker.scan_objects = sgv_scan_shrink;
#else
sgv_shrinker.shrink = sgv_shrink;
+1 -1
View File
@@ -1036,7 +1036,7 @@ out:
* the affected initiator.
*/
if (cmd != NULL)
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
#endif
}
-9
View File
@@ -412,15 +412,6 @@ static inline int scst_exec_req(struct scsi_device *sdev,
(void *)sgl, bufflen, nents, timeout, retries, privdata, done, gfp);
#endif
}
#else /* i.e. LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
static inline int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
void (*done)(void *data, char *sense, int result, int resid))
{
WARN_ON_ONCE(1);
return -1;
}
#endif
#endif
int scst_alloc_space(struct scst_cmd *cmd);
+1 -1
View File
@@ -2546,7 +2546,7 @@ static int scst_groups_devices_show(struct seq_file *seq, void *v)
list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
seq_printf(seq, "%-60s%-13lld%s\n",
acg_dev->dev->virt_name,
(long long unsigned int)acg_dev->lun,
(unsigned long long int)acg_dev->lun,
acg_dev->acg_dev_rd_only ? "RO" : "");
}
mutex_unlock(&scst_mutex);
+4 -1
View File
@@ -313,7 +313,9 @@ out:
#endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) && \
(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 || \
(RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 6))
/**
** Backported sysfs functions.
**/
@@ -1266,6 +1268,7 @@ static int __scst_process_luns_mgmt_store(char *buffer,
goto out_unlock;
} else if (virt_lun > SCST_MAX_LUN) {
PRINT_ERROR("Too big LUN %ld (max %d)", virt_lun, SCST_MAX_LUN);
res = -EINVAL;
goto out_unlock;
}
+59 -51
View File
@@ -157,7 +157,7 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
if (unlikely(cmd->unblock_dev)) {
TRACE_BLOCK("cmd %p (tag %llu): unblocking dev %s", cmd,
(long long unsigned int)cmd->tag, dev->virt_name);
(unsigned long long int)cmd->tag, dev->virt_name);
cmd->unblock_dev = 0;
scst_unblock_dev(dev);
} else if (unlikely(dev->strictly_serialized_cmd_waiting)) {
@@ -416,9 +416,9 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
TRACE_DBG("Preferred context: %d (cmd %p)", pref_context, cmd);
TRACE(TRACE_SCSI, "NEW CDB: len %d, lun %lld, initiator %s, "
"target %s, queue_type %x, tag %llu (cmd %p, sess %p)",
cmd->cdb_len, (long long unsigned int)cmd->lun,
cmd->cdb_len, (unsigned long long int)cmd->lun,
cmd->sess->initiator_name, cmd->tgt->tgt_name, cmd->queue_type,
(long long unsigned int)cmd->tag, cmd, sess);
(unsigned long long int)cmd->tag, cmd, sess);
PRINT_BUFF_FLAG(TRACE_SCSI, "CDB", cmd->cdb, cmd->cdb_len);
#ifdef CONFIG_SCST_EXTRACHECKS
@@ -655,6 +655,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
goto out;
case SCST_CMD_STATE_STOP:
/*
* !! cmd can be dead now!
*/
TRACE_DBG("Dev handler %s parse() requested stop "
"processing", devt->name);
res = SCST_CMD_STATE_RES_CONT_NEXT;
@@ -939,11 +942,11 @@ set_res:
TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_MINOR, "Atomic context and "
"non-WRITE data direction, rescheduling (cmd %p)", cmd);
res = SCST_CMD_STATE_RES_NEED_THREAD;
goto out;
/* go through */
}
#endif
out:
out_check_compl:
#ifdef CONFIG_SCST_EXTRACHECKS
if (unlikely(cmd->completed)) {
/* Command completed with error */
@@ -1000,6 +1003,7 @@ out:
}
}
out:
TRACE_EXIT_HRES(res);
return res;
@@ -1010,7 +1014,7 @@ out_hw_error:
out_done:
scst_set_cmd_abnormal_done_state(cmd);
res = SCST_CMD_STATE_RES_CONT_SAME;
goto out;
goto out_check_compl;
}
static void scst_set_write_len(struct scst_cmd *cmd)
@@ -1259,7 +1263,7 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status,
TRACE_DBG("Preferred context: %d", pref_context);
TRACE_DBG("tag=%llu, status=%#x",
(long long unsigned int)scst_cmd_get_tag(cmd),
(unsigned long long int)scst_cmd_get_tag(cmd),
status);
#ifdef CONFIG_SCST_EXTRACHECKS
@@ -1774,14 +1778,16 @@ static inline enum scst_exec_context scst_optimize_post_exec_context(
*/
void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid)
{
struct scst_cmd *cmd;
struct scst_cmd *cmd = data;
TRACE_ENTRY();
cmd = (struct scst_cmd *)data;
if (cmd == NULL)
goto out;
TRACE_DBG("cmd %p; CDB[0/%d] %#x: result %d; resid %d", cmd,
cmd->cdb_len, cmd->cdb[0], result, resid);
scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE, resid);
cmd->state = SCST_CMD_STATE_PRE_DEV_DONE;
@@ -2348,7 +2354,7 @@ static int scst_reserve_local(struct scst_cmd *cmd)
if ((cmd->cdb[0] == RESERVE_10) && (cmd->cdb[2] & SCST_RES_3RDPTY)) {
PRINT_ERROR("RESERVE_10: 3rdPty RESERVE not implemented "
"(lun=%lld)", (long long unsigned int)cmd->lun);
"(lun=%lld)", (unsigned long long int)cmd->lun);
scst_set_invalid_field_in_cdb(cmd, 2,
SCST_INVAL_FIELD_BIT_OFFS_VALID | 4);
goto out_done;
@@ -2983,14 +2989,16 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
sBUG_ON(res != SCST_EXEC_NOT_COMPLETED);
}
TRACE_DBG("Sending cmd %p to SCSI mid-level", cmd);
scsi_dev = dev->scsi_dev;
TRACE_DBG("Sending cmd %p to SCSI mid-level dev %d:%d:%d:%lld", cmd,
scsi_dev->host->host_no, scsi_dev->channel, scsi_dev->id,
(u64)scsi_dev->lun);
if (unlikely(scsi_dev == NULL)) {
PRINT_ERROR("Command for virtual device must be "
"processed by device handler (LUN %lld)!",
(long long unsigned int)cmd->lun);
(unsigned long long int)cmd->lun);
goto out_error;
}
@@ -3334,7 +3342,7 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd)
/* Necessary to allow aborting out of sn cmds */
TRACE_MGMT_DBG("Aborting out of sn cmd %p "
"(tag %llu, sn %u)", cmd,
(long long unsigned)cmd->tag, cmd->sn);
(unsigned long long)cmd->tag, cmd->sn);
order_data->def_cmd_count--;
scst_set_cmd_abnormal_done_state(cmd);
res = SCST_CMD_STATE_RES_CONT_SAME;
@@ -3414,7 +3422,7 @@ static int scst_check_sense(struct scst_cmd *cmd)
"detected for device %p", dev);
TRACE_DBG("Retrying cmd"
" %p (tag %llu)", cmd,
(long long unsigned)cmd->tag);
(unsigned long long)cmd->tag);
cmd->status = 0;
cmd->msg_status = 0;
@@ -3513,7 +3521,7 @@ static bool scst_check_auto_sense(struct scst_cmd *cmd)
"%s)", cmd->host_status, cmd, scst_get_opcode_name(cmd),
cmd->tgt->tgt_name, cmd->dev->virt_name);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
}
}
@@ -3541,7 +3549,7 @@ static int scst_pre_dev_done(struct scst_cmd *cmd)
PRINT_ERROR("%s", "Unable to issue REQUEST SENSE, "
"returning HARDWARE ERROR");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
}
goto out;
}
@@ -3577,7 +3585,7 @@ next:
"MODE_SENSE buffer");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(
scst_sense_hardw_error));
scst_sense_internal_failure));
err = true;
} else if (length > 2 && cmd->cdb[0] == MODE_SENSE)
address[2] |= 0x80; /* Write Protect*/
@@ -3609,7 +3617,7 @@ next:
PRINT_INFO("NormACA set for device: "
"lun=%lld, type 0x%02x. Clear it, "
"since it's unsupported.",
(long long unsigned int)cmd->lun,
(unsigned long long int)cmd->lun,
buffer[0]);
}
#endif
@@ -3618,7 +3626,7 @@ next:
PRINT_ERROR("%s", "Unable to get INQUIRY "
"buffer");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
SCST_LOAD_SENSE(scst_sense_internal_failure));
err = true;
}
if (buflen > 0)
@@ -3632,7 +3640,7 @@ next:
(cmd->cdb[0] == MODE_SELECT_10) ||
(cmd->cdb[0] == LOG_SELECT))) {
TRACE(TRACE_SCSI, "MODE/LOG SELECT succeeded (LUN %lld)",
(long long unsigned int)cmd->lun);
(unsigned long long int)cmd->lun);
cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS;
goto out;
}
@@ -3645,7 +3653,7 @@ next:
SCST_SENSE_ASCx_VALID,
0, 0x2a, 0x01)) {
TRACE(TRACE_SCSI, "MODE PARAMETERS CHANGED UA (lun "
"%lld)", (long long unsigned int)cmd->lun);
"%lld)", (unsigned long long int)cmd->lun);
cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS;
goto out;
}
@@ -3682,7 +3690,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
TRACE(TRACE_SCSI, "MODE/LOG SELECT succeeded, "
"setting the SELECT UA (lun=%lld)",
(long long unsigned int)cmd->lun);
(unsigned long long int)cmd->lun);
spin_lock_bh(&dev->dev_lock);
if (cmd->cdb[0] == LOG_SELECT) {
@@ -3728,7 +3736,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
TRACE(TRACE_SCSI, "Possible parameters changed UA %x "
"(LUN %lld): getting new parameters", cmd->sense[12],
(long long unsigned int)cmd->lun);
(unsigned long long int)cmd->lun);
scst_obtain_device_parameters(cmd->dev, NULL);
} else
@@ -3885,7 +3893,7 @@ again:
if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) {
EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags));
TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), "
"skipping", cmd, (long long unsigned int)cmd->tag);
"skipping", cmd, (unsigned long long int)cmd->tag);
cmd->state = SCST_CMD_STATE_FINISHED;
goto out_same;
}
@@ -4390,7 +4398,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
if (likely(!test_bit(SCST_FLAG_SUSPENDED, &scst_flags))) {
TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd,
(long long unsigned int)cmd->lun);
(unsigned long long int)cmd->lun);
res = -1;
tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
if (tgt_dev) {
@@ -4407,7 +4415,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
} else {
PRINT_INFO("Dev handler for device %lld is NULL, "
"the device will not be visible remotely",
(long long unsigned int)cmd->lun);
(unsigned long long int)cmd->lun);
nul_dev = true;
}
}
@@ -4416,7 +4424,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
TRACE(TRACE_MINOR,
"tgt_dev for LUN %lld not found, command to "
"unexisting LU (initiator %s, target %s)?",
(long long unsigned int)cmd->lun,
(unsigned long long int)cmd->lun,
cmd->sess->initiator_name, cmd->tgt->tgt_name);
}
scst_put(cmd->cpu_cmd_counter);
@@ -4559,7 +4567,7 @@ restart:
}
} else {
TRACE_MGMT_DBG("Aborting not inited cmd %p (tag %llu)",
cmd, (long long unsigned int)cmd->tag);
cmd, (unsigned long long int)cmd->tag);
scst_set_cmd_abnormal_done_state(cmd);
}
@@ -4781,7 +4789,7 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
res = SCST_CMD_STATE_RES_CONT_NEXT;
TRACE_MGMT_DBG("Skipping cmd %p (tag %llu), "
"because of TM DBG delay", cmd,
(long long unsigned int)cmd->tag);
(unsigned long long int)cmd->tag);
break;
}
res = scst_exec_check_sn(&cmd);
@@ -5029,7 +5037,7 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY();
TRACE_DBG("Finding tgt_dev for mgmt cmd %p (lun %lld)", mcmd,
(long long unsigned int)mcmd->lun);
(unsigned long long int)mcmd->lun);
res = scst_get_mgmt(mcmd);
if (unlikely(res != 0))
@@ -5060,7 +5068,7 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd)
TRACE_ENTRY();
TRACE_MGMT_DBG("cmd %p done (tag %llu)",
cmd, (long long unsigned int)cmd->tag);
cmd, (unsigned long long int)cmd->tag);
spin_lock_irqsave(&scst_mcmd_lock, flags);
@@ -5216,7 +5224,7 @@ void scst_finish_cmd_mgmt(struct scst_cmd *cmd)
TRACE_ENTRY();
TRACE(TRACE_MGMT, "Aborted cmd %p finished (tag %llu, ref %d)", cmd,
(long long unsigned int)cmd->tag, atomic_read(&cmd->cmd_ref));
(unsigned long long int)cmd->tag, atomic_read(&cmd->cmd_ref));
spin_lock_irqsave(&scst_mcmd_lock, flags);
@@ -5317,7 +5325,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
EXTRACHECKS_BUG_ON(!mcmd);
TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "Aborting cmd %p (tag %llu, op %s)",
cmd, (long long unsigned int)cmd->tag, scst_get_opcode_name(cmd));
cmd, (unsigned long long int)cmd->tag, scst_get_opcode_name(cmd));
/* To protect from concurrent aborts */
spin_lock_irqsave(&other_ini_lock, flags);
@@ -5399,7 +5407,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
if (cmd->sent_for_exec && !cmd->done) {
TRACE_MGMT_DBG("cmd %p (tag %llu) is being executed",
cmd, (long long unsigned int)cmd->tag);
cmd, (unsigned long long int)cmd->tag);
mstb->done_counted = 1;
mcmd->cmd_done_wait_count++;
}
@@ -5425,7 +5433,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
"deferring ABORT (cmd_done_wait_count %d, "
"cmd_finish_wait_count %d, internal %d, mcmd "
"fn %d (mcmd %p), initiator %s, target %s)",
cmd, (long long unsigned int)cmd->tag,
cmd, (unsigned long long int)cmd->tag,
cmd->sn, cmd->state, scst_get_opcode_name(cmd),
(long)(jiffies - cmd->start_time) / HZ,
cmd->timeout / HZ, mcmd->cmd_done_wait_count,
@@ -5649,7 +5657,7 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd)
struct scst_tgt_dev *tgt_dev = mcmd->mcmd_tgt_dev;
TRACE(TRACE_MGMT, "Aborting task set (lun=%lld, mcmd=%p)",
(long long unsigned int)tgt_dev->lun, mcmd);
(unsigned long long int)tgt_dev->lun, mcmd);
__scst_abort_task_set(mcmd, tgt_dev);
@@ -5681,7 +5689,7 @@ static bool scst_is_cmd_belongs_to_dev(struct scst_cmd *cmd,
TRACE_ENTRY();
TRACE_DBG("Finding match for dev %s and cmd %p (lun %lld)",
dev->virt_name, cmd, (long long unsigned int)cmd->lun);
dev->virt_name, cmd, (unsigned long long int)cmd->lun);
tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
res = tgt_dev && tgt_dev->dev == dev;
@@ -5701,7 +5709,7 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY();
TRACE(TRACE_MGMT, "Clearing task set (lun=%lld, mcmd=%p)",
(long long unsigned int)mcmd->lun, mcmd);
(unsigned long long int)mcmd->lun, mcmd);
#if 0 /* we are SAM-3 */
/*
@@ -5816,7 +5824,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
if (cmd == NULL) {
TRACE_MGMT_DBG("ABORT TASK: command "
"for tag %llu not found",
(long long unsigned int)mcmd->tag);
(unsigned long long int)mcmd->tag);
scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_TASK_NOT_EXIST);
spin_unlock_irq(&sess->sess_list_lock);
res = scst_set_mcmd_next_state(mcmd);
@@ -5828,7 +5836,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
mcmd->cpu_cmd_counter = scst_get();
spin_unlock_irq(&sess->sess_list_lock);
TRACE_DBG("Cmd to abort %p for tag %llu found (tgt_dev %p)",
cmd, (long long unsigned int)mcmd->tag, tgt_dev);
cmd, (unsigned long long int)mcmd->tag, tgt_dev);
mcmd->cmd_to_abort = cmd;
sBUG_ON(mcmd->mcmd_tgt_dev != NULL);
mcmd->mcmd_tgt_dev = tgt_dev;
@@ -5870,7 +5878,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
mcmd->state = SCST_MCMD_STATE_EXEC;
else if (rc < 0) {
PRINT_ERROR("Corresponding device for LUN %lld not "
"found", (long long unsigned int)mcmd->lun);
"found", (unsigned long long int)mcmd->lun);
scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_LUN_NOT_EXIST);
res = scst_set_mcmd_next_state(mcmd);
} else
@@ -6000,7 +6008,7 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY();
TRACE(TRACE_MGMT, "Resetting LUN %lld (mcmd %p)",
(long long unsigned int)tgt_dev->lun, mcmd);
(unsigned long long int)tgt_dev->lun, mcmd);
mcmd->needs_unblocking = 1;
@@ -6185,21 +6193,21 @@ static int scst_abort_task(struct scst_mgmt_cmd *mcmd)
TRACE_MGMT_DBG("Aborting task (cmd %p, sn %d, set %d, tag %llu, "
"queue_type %x)", cmd, cmd->sn, cmd->sn_set,
(long long unsigned int)mcmd->tag, cmd->queue_type);
(unsigned long long int)mcmd->tag, cmd->queue_type);
if (mcmd->lun_set && (mcmd->lun != cmd->lun)) {
PRINT_ERROR("ABORT TASK: LUN mismatch: mcmd LUN %llx, "
"cmd LUN %llx, cmd tag %llu",
(long long unsigned int)mcmd->lun,
(long long unsigned int)cmd->lun,
(long long unsigned int)mcmd->tag);
(unsigned long long int)mcmd->lun,
(unsigned long long int)cmd->lun,
(unsigned long long int)mcmd->tag);
scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_REJECTED);
} else if (mcmd->cmd_sn_set &&
(scst_sn_before(mcmd->cmd_sn, cmd->tgt_sn) ||
(mcmd->cmd_sn == cmd->tgt_sn))) {
PRINT_ERROR("ABORT TASK: SN mismatch: mcmd SN %x, "
"cmd SN %x, cmd tag %llu", mcmd->cmd_sn,
cmd->tgt_sn, (long long unsigned int)mcmd->tag);
cmd->tgt_sn, (unsigned long long int)mcmd->tag);
scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_REJECTED);
} else {
spin_lock_irq(&cmd->sess->sess_list_lock);
@@ -6758,9 +6766,9 @@ int scst_rx_mgmt_fn(struct scst_session *sess,
TRACE_MGMT_DBG("sess=%p, tag_set %d, tag %lld, lun_set %d, "
"lun=%lld, cmd_sn_set %d, cmd_sn %d, priv %p", sess,
params->tag_set,
(long long unsigned int)params->tag,
(unsigned long long int)params->tag,
params->lun_set,
(long long unsigned int)mcmd->lun,
(unsigned long long int)mcmd->lun,
params->cmd_sn_set,
params->cmd_sn,
params->tgt_priv);
@@ -7451,7 +7459,7 @@ static struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess,
/* ToDo: hash list */
TRACE_DBG("%s (sess=%p, tag=%llu)", "Searching in sess cmd list",
sess, (long long unsigned int)tag);
sess, (unsigned long long int)tag);
list_for_each_entry(cmd, &sess->sess_cmd_list,
sess_cmd_list_entry) {
+2
View File
@@ -0,0 +1,2 @@
obj-$(CONFIG_SCST_LOCAL) += scst_local.o
+2
View File
@@ -0,0 +1,2 @@
obj-$(CONFIG_SCST_LOCAL) += scst_local.o

Some files were not shown because too many files have changed in this diff Show More