lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20070201230752.GA17679@dreamland.darkstar.lan>
Date:	Fri, 2 Feb 2007 00:07:52 +0100
From:	Luca Tettamanti <kronos.it@...il.com>
To:	Adrian Bunk <bunk@...sta.de>
Cc:	Peter Osterlund <petero2@...ia.com>, linux-kernel@...r.kernel.org
Subject: Re: [2.6.20-rc6] pktcdvd doesn't work

Il Thu, Feb 01, 2007 at 12:30:44AM +0100, Adrian Bunk ha scritto: 
> On Tue, Jan 30, 2007 at 08:53:19PM +0100, Luca Tettamanti wrote:
> > Hi,
> > pktcdvd on kernel 2.6.20-rc6 is not working as expected. Any file that
> > is written to the device is lost after umount.
> > I rarely use pktcdvd but at some point it used to work on my system.
> > 
> > This is what I'm doing:
> > 
> > root@...amland:/tmp# cdrwtool -d /dev/scd0 -q
> > using device /dev/scd0
> > 1029KB internal buffer
> > setting write speed to 12x
> > Settings for /dev/scd0:
> >         Fixed packets, size 32
> >         Mode-2 disc
> >...
> 
> Does 2.6.20-rc7 work?
> If no, does it work after applying the attached patch?
> If no, does 2.6.19.2 work?

Git current + the following patch works.

> diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
> index 6246219..7c95c76 100644
> --- a/drivers/block/pktcdvd.c
> +++ b/drivers/block/pktcdvd.c
> @@ -765,34 +765,47 @@ static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio
>   */
>  static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
>  {
> -	request_queue_t *q = bdev_get_queue(pd->bdev);
> +	char sense[SCSI_SENSE_BUFFERSIZE];
> +	request_queue_t *q;
>  	struct request *rq;
> -	int ret = 0;
> -
> -	rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ?
> -			     WRITE : READ, __GFP_WAIT);
> -
> -	if (cgc->buflen) {
> -		if (blk_rq_map_kern(q, rq, cgc->buffer, cgc->buflen, __GFP_WAIT))
> -			goto out;
> -	}
> +	DECLARE_COMPLETION_ONSTACK(wait);
> +	int err = 0;
>  
> -	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
> -	memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
> -	if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
> -		memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
> +	q = bdev_get_queue(pd->bdev);
>  
> +	rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ,
> +			     __GFP_WAIT);
> +	rq->errors = 0;
> +	rq->rq_disk = pd->bdev->bd_disk;
> +	rq->bio = NULL;
> +	rq->buffer = NULL;
>  	rq->timeout = 60*HZ;
> +	rq->data = cgc->buffer;
> +	rq->data_len = cgc->buflen;
> +	rq->sense = sense;
> +	memset(sense, 0, sizeof(sense));
> +	rq->sense_len = 0;
>  	rq->cmd_type = REQ_TYPE_BLOCK_PC;
>  	rq->cmd_flags |= REQ_HARDBARRIER;
>  	if (cgc->quiet)
>  		rq->cmd_flags |= REQ_QUIET;
> +	memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
> +	if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
> +		memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
> +	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
> +
> +	rq->ref_count++;
> +	rq->end_io_data = &wait;
> +	rq->end_io = blk_end_sync_rq;
> +	elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
> +	generic_unplug_device(q);
> +	wait_for_completion(&wait);
> +
> +	if (rq->errors)
> +		err = -EIO;
>  
> -	blk_execute_rq(rq->q, pd->bdev->bd_disk, rq, 0);
> -	ret = rq->errors;
> -out:
>  	blk_put_request(rq);
> -	return ret;
> +	return err;
>  }
>  
>  /*
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index f02f48a..6fe1e82 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -998,14 +998,25 @@ static int scsi_init_io(struct scsi_cmnd *cmd)
>  	int		   count;
>  
>  	/*
> -	 * We used to not use scatter-gather for single segment request,
> +	 * if this is a rq->data based REQ_BLOCK_PC, setup for a non-sg xfer
> +	 */
> +	if (blk_pc_request(req) && !req->bio) {
> +		cmd->request_bufflen = req->data_len;
> +		cmd->request_buffer = req->data;
> +		req->buffer = req->data;
> +		cmd->use_sg = 0;
> +		return 0;
> +	}
> +
> +	/*
> +	 * we used to not use scatter-gather for single segment request,
>  	 * but now we do (it makes highmem I/O easier to support without
>  	 * kmapping pages)
>  	 */
>  	cmd->use_sg = req->nr_phys_segments;
>  
>  	/*
> -	 * If sg table allocation fails, requeue request later.
> +	 * if sg table allocation fails, requeue request later.
>  	 */
>  	sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC);
>  	if (unlikely(!sgpnt)) {
> @@ -1013,21 +1024,24 @@ static int scsi_init_io(struct scsi_cmnd *cmd)
>  		return BLKPREP_DEFER;
>  	}
>  
> -	req->buffer = NULL;
>  	cmd->request_buffer = (char *) sgpnt;
> +	cmd->request_bufflen = req->nr_sectors << 9;
>  	if (blk_pc_request(req))
>  		cmd->request_bufflen = req->data_len;
> -	else
> -		cmd->request_bufflen = req->nr_sectors << 9;
> +	req->buffer = NULL;
>  
>  	/* 
>  	 * Next, walk the list, and fill in the addresses and sizes of
>  	 * each segment.
>  	 */
>  	count = blk_rq_map_sg(req->q, req, cmd->request_buffer);
> +
> +	/*
> +	 * mapped well, send it off
> +	 */
>  	if (likely(count <= cmd->use_sg)) {
>  		cmd->use_sg = count;
> -		return BLKPREP_OK;
> +		return 0;
>  	}
>  
>  	printk(KERN_ERR "Incorrect number of segments after building list\n");
> @@ -1057,27 +1071,6 @@ static int scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk,
>  	return -EOPNOTSUPP;
>  }
>  
> -static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev,
> -		struct request *req)
> -{
> -	struct scsi_cmnd *cmd;
> -
> -	if (!req->special) {
> -		cmd = scsi_get_command(sdev, GFP_ATOMIC);
> -		if (unlikely(!cmd))
> -			return NULL;
> -		req->special = cmd;
> -	} else {
> -		cmd = req->special;
> -	}
> -
> -	/* pull a tag out of the request if we have one */
> -	cmd->tag = req->tag;
> -	cmd->request = req;
> -
> -	return cmd;
> -}
> -
>  static void scsi_blk_pc_done(struct scsi_cmnd *cmd)
>  {
>  	BUG_ON(!blk_pc_request(cmd->request));
> @@ -1090,37 +1083,9 @@ static void scsi_blk_pc_done(struct scsi_cmnd *cmd)
>  	scsi_io_completion(cmd, cmd->request_bufflen);
>  }
>  
> -static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
> +static void scsi_setup_blk_pc_cmnd(struct scsi_cmnd *cmd)
>  {
> -	struct scsi_cmnd *cmd;
> -
> -	cmd = scsi_get_cmd_from_req(sdev, req);
> -	if (unlikely(!cmd))
> -		return BLKPREP_DEFER;
> -
> -	/*
> -	 * BLOCK_PC requests may transfer data, in which case they must
> -	 * a bio attached to them.  Or they might contain a SCSI command
> -	 * that does not transfer data, in which case they may optionally
> -	 * submit a request without an attached bio.
> -	 */
> -	if (req->bio) {
> -		int ret;
> -
> -		BUG_ON(!req->nr_phys_segments);
> -
> -		ret = scsi_init_io(cmd);
> -		if (unlikely(ret))
> -			return ret;
> -	} else {
> -		BUG_ON(req->data_len);
> -		BUG_ON(req->data);
> -
> -		cmd->request_bufflen = 0;
> -		cmd->request_buffer = NULL;
> -		cmd->use_sg = 0;
> -		req->buffer = NULL;
> -	}
> +	struct request *req = cmd->request;
>  
>  	BUILD_BUG_ON(sizeof(req->cmd) > sizeof(cmd->cmnd));
>  	memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd));
> @@ -1136,138 +1101,154 @@ static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
>  	cmd->allowed = req->retries;
>  	cmd->timeout_per_command = req->timeout;
>  	cmd->done = scsi_blk_pc_done;
> -	return BLKPREP_OK;
> -}
> -
> -/*
> - * Setup a REQ_TYPE_FS command.  These are simple read/write request
> - * from filesystems that still need to be translated to SCSI CDBs from
> - * the ULD.
> - */
> -static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
> -{
> -	struct scsi_cmnd *cmd;
> -	struct scsi_driver *drv;
> -	int ret;
> -
> -	/*
> -	 * Filesystem requests must transfer data.
> -	 */
> -	BUG_ON(!req->nr_phys_segments);
> -
> -	cmd = scsi_get_cmd_from_req(sdev, req);
> -	if (unlikely(!cmd))
> -		return BLKPREP_DEFER;
> -
> -	ret = scsi_init_io(cmd);
> -	if (unlikely(ret))
> -		return ret;
> -
> -	/*
> -	 * Initialize the actual SCSI command for this request.
> -	 */
> -	drv = *(struct scsi_driver **)req->rq_disk->private_data;
> -	if (unlikely(!drv->init_command(cmd))) {
> -		scsi_release_buffers(cmd);
> -		scsi_put_command(cmd);
> -		return BLKPREP_KILL;
> -	}
> -
> -	return BLKPREP_OK;
>  }
>  
>  static int scsi_prep_fn(struct request_queue *q, struct request *req)
>  {
>  	struct scsi_device *sdev = q->queuedata;
> -	int ret = BLKPREP_OK;
> +	struct scsi_cmnd *cmd;
> +	int specials_only = 0;
>  
>  	/*
> -	 * If the device is not in running state we will reject some
> -	 * or all commands.
> +	 * Just check to see if the device is online.  If it isn't, we
> +	 * refuse to process any commands.  The device must be brought
> +	 * online before trying any recovery commands
>  	 */
> +	if (unlikely(!scsi_device_online(sdev))) {
> +		sdev_printk(KERN_ERR, sdev,
> +			    "rejecting I/O to offline device\n");
> +		goto kill;
> +	}
>  	if (unlikely(sdev->sdev_state != SDEV_RUNNING)) {
> -		switch (sdev->sdev_state) {
> -		case SDEV_OFFLINE:
> -			/*
> -			 * If the device is offline we refuse to process any
> -			 * commands.  The device must be brought online
> -			 * before trying any recovery commands.
> -			 */
> -			sdev_printk(KERN_ERR, sdev,
> -				    "rejecting I/O to offline device\n");
> -			ret = BLKPREP_KILL;
> -			break;
> -		case SDEV_DEL:
> -			/*
> -			 * If the device is fully deleted, we refuse to
> -			 * process any commands as well.
> -			 */
> +		/* OK, we're not in a running state don't prep
> +		 * user commands */
> +		if (sdev->sdev_state == SDEV_DEL) {
> +			/* Device is fully deleted, no commands
> +			 * at all allowed down */
>  			sdev_printk(KERN_ERR, sdev,
>  				    "rejecting I/O to dead device\n");
> -			ret = BLKPREP_KILL;
> -			break;
> -		case SDEV_QUIESCE:
> -		case SDEV_BLOCK:
> -			/*
> -			 * If the devices is blocked we defer normal commands.
> -			 */
> -			if (!(req->cmd_flags & REQ_PREEMPT))
> -				ret = BLKPREP_DEFER;
> -			break;
> -		default:
> -			/*
> -			 * For any other not fully online state we only allow
> -			 * special commands.  In particular any user initiated
> -			 * command is not allowed.
> -			 */
> -			if (!(req->cmd_flags & REQ_PREEMPT))
> -				ret = BLKPREP_KILL;
> -			break;
> +			goto kill;
>  		}
> -
> -		if (ret != BLKPREP_OK)
> -			goto out;
> +		/* OK, we only allow special commands (i.e. not
> +		 * user initiated ones */
> +		specials_only = sdev->sdev_state;
>  	}
>  
> -	switch (req->cmd_type) {
> -	case REQ_TYPE_BLOCK_PC:
> -		ret = scsi_setup_blk_pc_cmnd(sdev, req);
> -		break;
> -	case REQ_TYPE_FS:
> -		ret = scsi_setup_fs_cmnd(sdev, req);
> -		break;
> -	default:
> +	/*
> +	 * Find the actual device driver associated with this command.
> +	 * The SPECIAL requests are things like character device or
> +	 * ioctls, which did not originate from ll_rw_blk.  Note that
> +	 * the special field is also used to indicate the cmd for
> +	 * the remainder of a partially fulfilled request that can 
> +	 * come up when there is a medium error.  We have to treat
> +	 * these two cases differently.  We differentiate by looking
> +	 * at request->cmd, as this tells us the real story.
> +	 */
> +	if (blk_special_request(req) && req->special)
> +		cmd = req->special;
> +	else if (blk_pc_request(req) || blk_fs_request(req)) {
> +		if (unlikely(specials_only) && !(req->cmd_flags & REQ_PREEMPT)){
> +			if (specials_only == SDEV_QUIESCE ||
> +			    specials_only == SDEV_BLOCK)
> +				goto defer;
> +			
> +			sdev_printk(KERN_ERR, sdev,
> +				    "rejecting I/O to device being removed\n");
> +			goto kill;
> +		}
> +			
>  		/*
> -		 * All other command types are not supported.
> -		 *
> -		 * Note that these days the SCSI subsystem does not use
> -		 * REQ_TYPE_SPECIAL requests anymore.  These are only used
> -		 * (directly or via blk_insert_request) by non-SCSI drivers.
> +		 * Now try and find a command block that we can use.
>  		 */
> +		if (!req->special) {
> +			cmd = scsi_get_command(sdev, GFP_ATOMIC);
> +			if (unlikely(!cmd))
> +				goto defer;
> +		} else
> +			cmd = req->special;
> +		
> +		/* pull a tag out of the request if we have one */
> +		cmd->tag = req->tag;
> +	} else {
>  		blk_dump_rq_flags(req, "SCSI bad req");
> -		ret = BLKPREP_KILL;
> -		break;
> +		goto kill;
>  	}
> +	
> +	/* note the overloading of req->special.  When the tag
> +	 * is active it always means cmd.  If the tag goes
> +	 * back for re-queueing, it may be reset */
> +	req->special = cmd;
> +	cmd->request = req;
> +	
> +	/*
> +	 * FIXME: drop the lock here because the functions below
> +	 * expect to be called without the queue lock held.  Also,
> +	 * previously, we dequeued the request before dropping the
> +	 * lock.  We hope REQ_STARTED prevents anything untoward from
> +	 * happening now.
> +	 */
> +	if (blk_fs_request(req) || blk_pc_request(req)) {
> +		int ret;
>  
> - out:
> -	switch (ret) {
> -	case BLKPREP_KILL:
> -		req->errors = DID_NO_CONNECT << 16;
> -		break;
> -	case BLKPREP_DEFER:
>  		/*
> -		 * If we defer, the elv_next_request() returns NULL, but the
> -		 * queue must be restarted, so we plug here if no returning
> -		 * command will automatically do that.
> +		 * This will do a couple of things:
> +		 *  1) Fill in the actual SCSI command.
> +		 *  2) Fill in any other upper-level specific fields
> +		 * (timeout).
> +		 *
> +		 * If this returns 0, it means that the request failed
> +		 * (reading past end of disk, reading offline device,
> +		 * etc).   This won't actually talk to the device, but
> +		 * some kinds of consistency checking may cause the	
> +		 * request to be rejected immediately.
>  		 */
> -		if (sdev->device_busy == 0)
> -			blk_plug_device(q);
> -		break;
> -	default:
> -		req->cmd_flags |= REQ_DONTPREP;
> +
> +		/* 
> +		 * This sets up the scatter-gather table (allocating if
> +		 * required).
> +		 */
> +		ret = scsi_init_io(cmd);
> +		switch(ret) {
> +			/* For BLKPREP_KILL/DEFER the cmd was released */
> +		case BLKPREP_KILL:
> +			goto kill;
> +		case BLKPREP_DEFER:
> +			goto defer;
> +		}
> +		
> +		/*
> +		 * Initialize the actual SCSI command for this request.
> +		 */
> +		if (blk_pc_request(req)) {
> +			scsi_setup_blk_pc_cmnd(cmd);
> +		} else if (req->rq_disk) {
> +			struct scsi_driver *drv;
> +
> +			drv = *(struct scsi_driver **)req->rq_disk->private_data;
> +			if (unlikely(!drv->init_command(cmd))) {
> +				scsi_release_buffers(cmd);
> +				scsi_put_command(cmd);
> +				goto kill;
> +			}
> +		}
>  	}
>  
> -	return ret;
> +	/*
> +	 * The request is now prepped, no need to come back here
> +	 */
> +	req->cmd_flags |= REQ_DONTPREP;
> +	return BLKPREP_OK;
> +
> + defer:
> +	/* If we defer, the elv_next_request() returns NULL, but the
> +	 * queue must be restarted, so we plug here if no returning
> +	 * command will automatically do that. */
> +	if (sdev->device_busy == 0)
> +		blk_plug_device(q);
> +	return BLKPREP_DEFER;
> + kill:
> +	req->errors = DID_NO_CONNECT << 16;
> +	return BLKPREP_KILL;
>  }
>  
>  /*

Correct capacity is reported:

pktcdvd: Fixed packets, 32 blocks, Mode-2 disc
pktcdvd: Max. media speed: 4
pktcdvd: write speed 4x
pktcdvd: 551232kB available on disc

and writing works fine.
While tracking this bug I've enabled lockdep, which complains when I
create the device (pktsetup ptk0 /dev/scd0); the warning appears also
with vanilla kernel:

pktcdvd: writer pktcdvd0 mapped to sr0

=============================================
[ INFO: possible recursive locking detected ]
2.6.20-rc7 #24
---------------------------------------------
vol_id/5364 is trying to acquire lock:
 (&bdev->bd_mutex){--..}, at: [<b017e587>] do_open+0x64/0x280

but task is already holding lock:
 (&bdev->bd_mutex){--..}, at: [<b017e587>] do_open+0x64/0x280

other info that might help us debug this:
2 locks held by vol_id/5364:
 #0:  (&bdev->bd_mutex){--..}, at: [<b017e587>] do_open+0x64/0x280
 #1:  (&ctl_mutex#2){--..}, at: [<f1a69bfb>] pkt_open+0x1a/0xcaa [pktcdvd]

stack backtrace:
 [<b0136a69>] __lock_acquire+0x11e/0xa09
 [<b02f17e3>] __mutex_unlock_slowpath+0x105/0x127
 [<b0137635>] lock_acquire+0x56/0x6e
 [<b017e587>] do_open+0x64/0x280
 [<b02f1b83>] mutex_lock_nested+0xf8/0x27c
 [<b017e587>] do_open+0x64/0x280
 [<b017e587>] do_open+0x64/0x280
 [<b017a427>] __find_get_block+0x158/0x162
 [<b017e7fe>] __blkdev_get+0x5b/0x66
 [<b017e81b>] blkdev_get+0x12/0x14
 [<f1a69c6e>] pkt_open+0x8d/0xcaa [pktcdvd]
 [<b02f317b>] _spin_unlock+0x25/0x3b
 [<b02f17e3>] __mutex_unlock_slowpath+0x105/0x127
 [<b0136545>] trace_hardirqs_on+0x11e/0x141
 [<b0165486>] do_lookup+0x4f/0x143
 [<b016db3b>] dput+0x2c/0x12c
 [<b016d61f>] __d_lookup+0x6a/0x10b
 [<b0172567>] lookup_mnt+0x10/0x37
 [<b016d61f>] __d_lookup+0x6a/0x10b
 [<b02f317b>] _spin_unlock+0x25/0x3b
 [<b016d6a3>] __d_lookup+0xee/0x10b
 [<b0165486>] do_lookup+0x4f/0x143
 [<b016db3b>] dput+0x2c/0x12c
 [<b0166e9a>] __link_path_walk+0xa13/0xb57
 [<b024a2b5>] kobj_lookup+0x33/0x14d
 [<b017e587>] do_open+0x64/0x280
 [<b02f1ce1>] mutex_lock_nested+0x256/0x27c
 [<b0136364>] mark_held_locks+0x46/0x62
 [<b02f1ce1>] mutex_lock_nested+0x256/0x27c
 [<b02f1ce1>] mutex_lock_nested+0x256/0x27c
 [<b0136545>] trace_hardirqs_on+0x11e/0x141
 [<b02f1cff>] mutex_lock_nested+0x274/0x27c
 [<b017e587>] do_open+0x64/0x280
 [<b017e5b4>] do_open+0x91/0x280
 [<b017e92d>] blkdev_open+0x0/0x4d
 [<b017e952>] blkdev_open+0x25/0x4d
 [<b015ddc8>] __dentry_open+0x101/0x1b9
 [<b015defa>] nameidata_to_filp+0x24/0x33
 [<b015df3b>] do_filp_open+0x32/0x39
 [<b02f317b>] _spin_unlock+0x25/0x3b
 [<b015dcbd>] get_unused_fd+0xb3/0xbd
 [<b015df84>] do_sys_open+0x42/0xc3
 [<b015e03e>] sys_open+0x1c/0x1e
 [<b0102ee4>] syscall_call+0x7/0xb
 =======================
pktcdvd: Fixed packets, 32 blocks, Mode-2 disc
pktcdvd: Max. media speed: 4
pktcdvd: write speed 4x
pktcdvd: 551232kB available on disc

Luca
-- 
The trouble with computers is that they do what you tell them, 
not what you want.
D. Cohen
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ