All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luca Tettamanti <kronos.it@gmail.com>
To: Adrian Bunk <bunk@stusta.de>
Cc: Peter Osterlund <petero2@telia.com>, linux-kernel@vger.kernel.org
Subject: Re: [2.6.20-rc6] pktcdvd doesn't work
Date: Fri, 2 Feb 2007 00:07:52 +0100	[thread overview]
Message-ID: <20070201230752.GA17679@dreamland.darkstar.lan> (raw)
In-Reply-To: <20070131233044.GX3754@stusta.de>

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@dreamland:/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

  reply	other threads:[~2007-02-01 23:07 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-01-30 19:53 [2.6.20-rc6] pktcdvd doesn't work Luca Tettamanti
2007-01-30 20:02 ` Jan Engelhardt
2007-01-30 20:36   ` Luca Tettamanti
2007-01-30 20:42     ` Jan Engelhardt
2007-01-30 23:16       ` Luca Tettamanti
2007-01-31 10:58         ` Jeff Garzik
2007-01-31 19:17           ` Fabio Comolli
2007-01-31 19:59           ` Luca
2007-01-31 23:10           ` Adrian Bunk
2007-01-31 23:38             ` Luca Tettamanti
2007-01-31 23:30 ` Adrian Bunk
2007-02-01 23:07   ` Luca Tettamanti [this message]
2007-02-02  2:21     ` Andrew Morton
2007-02-02  2:33       ` Adrian Bunk
2007-02-02  4:07         ` Andrew Morton
2007-02-02  5:32           ` Adrian Bunk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070201230752.GA17679@dreamland.darkstar.lan \
    --to=kronos.it@gmail.com \
    --cc=bunk@stusta.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=petero2@telia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.