LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: oakad@exemail.com.au
Cc: linux-kernel@vger.kernel.org, Alex Dubov <oakad@yahoo.com>,
	Jens Axboe <jens.axboe@oracle.com>
Subject: Re: [PATCH] [MEMSTICK] Initial commit for Sony MemoryStick support
Date: Thu, 10 Jan 2008 01:00:49 -0800	[thread overview]
Message-ID: <20080110010049.695da076.akpm@linux-foundation.org> (raw)
In-Reply-To: <1199256144-1019-1-git-send-email-oakad@exemail.com.au>

On Wed,  2 Jan 2008 17:42:24 +1100 oakad@exemail.com.au wrote:

> From: Alex Dubov <oakad@yahoo.com>
> 
> Sony MemoryStick cards are used in many products manufactured by Sony. They
> are available both as storage and as IO expansion cards. Currently, only
> MemoryStick Pro storage cards are supported via TI FlashMedia MemoryStick
> interface.
> 

Will you be running a git tree for this?  If so, please send me the link.

Will this enable that thus-far useless slot on my Vaio?

Where did the info come from which enabled this driver to be written?  I
thought Sony were super-secretive about this stuff?


> @@ -0,0 +1,26 @@
> +#
> +# MemoryStick subsystem configuration
> +#
> +
> +menuconfig MEMSTICK
> +	tristate "Sony MemoryStick card support (EXPERIMENTAL)"
> +	help
> +	  Sony MemoryStick is a proprietary storage/extension card protocol.
> +
> +	  If you want MemoryStick support, you should say Y here and also
> +	  to the specific driver for your MMC interface.

Are you sure this has enough dependencies?  CONFIG_TIFM_* for a start?

<fiddles with Kconfig>

We can create a config which has CONFIG_TIFM_CORE=m, CONFIG_MEMSTICK=y. 
That might not work?

Anyway, please have a think about that.

> +static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env)
> +{
> +	struct memstick_dev *card = container_of(dev, struct memstick_dev,
> +						  dev);
> +
> +	if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type))
> +		return -ENOMEM;
> +
> +	if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category))
> +		return -ENOMEM;

I trust higher-level code cleans up the MEMSTICK_TYPE string if this call
fails.

> +	if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class))
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static int memstick_device_probe(struct device *dev)
> +{
> +	struct memstick_dev *card = container_of(dev, struct memstick_dev,
> +						 dev);
> +	struct memstick_driver *drv = container_of(dev->driver,
> +						   struct memstick_driver,
> +						   driver);
> +	int rc = -ENODEV;
> +
> +	get_device(dev);
> +	if (dev->driver && drv->probe) {
> +		rc = drv->probe(card);
> +		if (!rc)
> +			return 0;
> +	}
> +	put_device(dev);
> +	return rc;
> +}

Could just do:

	int rc = -ENODEV;
	if (dev->driver && drv->probe) {
		rc = drv->probe(card);
		if (!rc)
			get_device(dev);
	}
	return rc;

If the earlier get_device() is indeed needed then we already have a
problem..

> +
> +static int memstick_device_remove(struct device *dev)
> +{
> +	struct memstick_dev *card = container_of(dev, struct memstick_dev,
> +						  dev);
> +	struct memstick_driver *drv = container_of(dev->driver,
> +						   struct memstick_driver,
> +						   driver);
> +
> +	if (dev->driver && drv->remove) {
> +		drv->remove(card);
> +		card->dev.driver = NULL;

Is this needed?

> +	}
> +
> +	put_device(dev);
> +	return 0;
> +}
>
> ...
>
> +void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
> +		       void *buf, size_t length)
> +{
> +	mrq->tpc = tpc;
> +	if (tpc & 8)
> +		mrq->data_dir = WRITE;
> +	else
> +		mrq->data_dir = READ;
> +
> +	mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length;
> +	if (mrq->data_dir == WRITE)
> +		memcpy(mrq->data, buf, mrq->data_len);
> +
> +	mrq->io_type = MEMSTICK_IO_VAL;
> +
> +	if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
> +		mrq->need_card_int = 1;
> +	else
> +		mrq->need_card_int = 0;
> +
> +	mrq->get_int_reg = 0;
> +}
> +EXPORT_SYMBOL(memstick_init_req);

It's kinda polite to add some commentary to global, exported-to-modules
functions.

It leads to more effective code review too.  Reviewing code is the process
of determining whether implementation != intention.  But without comments,
the reviewer's starting point is intention = implementation.  So we end up
incorrectly returning true ;)

> +static int h_memstick_read_dev_id(struct memstick_dev *card,
> +				  struct memstick_request **mrq)
> +{
> +	struct ms_id_register id_reg;
> +
> +	if (!(*mrq)) {
> +		memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL,
> +				  sizeof(struct ms_id_register));
> +		*mrq = &card->current_mrq;
> +		return 0;
> +	} else {
> +		if (!(*mrq)->error) {
> +			memcpy(&id_reg, (*mrq)->data, sizeof(id_reg));
> +			card->id = (struct memstick_device_id){
> +				.match_flags = MEMSTICK_MATCH_ALL,
> +				.type = id_reg.type,
> +				.category = id_reg.category,
> +				.class = id_reg.class
> +			};
> +		}
> +		complete(&card->mrq_complete);
> +		return -EAGAIN;
> +	}
> +}

Without comments this little reader is mystified as to why this function
would return -EAGAIN, and what such a thing means.

> +static int h_memstick_set_rw_addr(struct memstick_dev *card,
> +				  struct memstick_request **mrq)
> +{
> +	if (!(*mrq)) {
> +		memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS,
> +				  (char *)&card->reg_addr,
> +				  sizeof(card->reg_addr));
> +		*mrq = &card->current_mrq;
> +		return 0;
> +	} else {
> +		complete(&card->mrq_complete);
> +		return -EAGAIN;
> +	}
> +}
> +
> +int memstick_set_rw_addr(struct memstick_dev *card)
> +{
> +	card->next_request = h_memstick_set_rw_addr;
> +	memstick_new_req(card->host);
> +	wait_for_completion(&card->mrq_complete);
> +
> +	return card->current_mrq.error;
> +}
> +EXPORT_SYMBOL(memstick_set_rw_addr);
> +
> +static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
> +{
> +	struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
> +					    GFP_KERNEL);
> +	struct memstick_dev *old_card = host->card;
> +	struct ms_id_register id_reg;
> +
> +	if (card) {
> +		card->host = host;
> +		snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
> +			 "%s", host->cdev.class_id);
> +		card->dev.parent = host->cdev.dev;
> +		card->dev.bus = &memstick_bus_type;
> +		card->dev.release = memstick_free_card;
> +		card->check = memstick_dummy_check;
> +
> +		card->reg_addr = (struct ms_register_addr){
> +			offsetof(struct ms_register, id),
> +			sizeof(id_reg),
> +			offsetof(struct ms_register, id),
> +			sizeof(id_reg)
> +		};

You may find that gcc generates crappy code for this: assembles a struct on
the stack them does a memcpy (or even a memb-er-by-member copy).  Doing
member-at-a-time assignment would produce a better result.  If you care much.


> +		init_completion(&card->mrq_complete);
> +
> +		host->card = card;
> +		if (memstick_set_rw_addr(card))
> +			goto err_out;
> +
> +		card->next_request = h_memstick_read_dev_id;
> +		memstick_new_req(host);
> +		wait_for_completion(&card->mrq_complete);
> +
> +		if (card->current_mrq.error)
> +			goto err_out;
> +	}
> +	host->card = old_card;
> +	return card;
> +err_out:
> +	host->card = old_card;
> +	kfree(card);
> +	return NULL;
> +}
> +
>
> ...
>
> +
> +struct mspro_sys_attr {
> +	size_t                  size;
> +	unsigned char           *data;
> +	unsigned char           id;
> +	char                    name[32];
> +	struct device_attribute sys_attr;
> +};
> +
> +struct mspro_attr_entry {
> +	unsigned int  address;
> +	unsigned int  size;
> +	unsigned char id;
> +	unsigned char reserved[3];
> +} __attribute__((packed));
> +
> +struct mspro_attribute {
> +	unsigned short          signature;
> +	unsigned short          version;
> +	unsigned char           count;
> +	unsigned char           reserved[11];
> +	struct mspro_attr_entry entries[];
> +} __attribute__((packed));

Why are these packed?  Do they map onto something whcih hardware knows
about?

Again, the lack of comments leaves me at a loss.

>
> ...
>
> +struct mspro_block_data {
> +	struct memstick_dev   *card;
> +	unsigned int          usage_count;
> +	struct gendisk        *disk;
> +	struct request_queue  *queue;
> +	spinlock_t            q_lock;
> +	wait_queue_head_t     q_wait;
> +	struct task_struct    *q_thread;
> +
> +	unsigned short        page_size;
> +	unsigned short        cylinders;
> +	unsigned short        heads;
> +	unsigned short        sectors_per_track;
> +
> +	unsigned char         system;
> +	unsigned char         read_only:1,
> +			      active:1,
> +			      has_request:1,
> +			      data_dir:1;

You're aware that these four fields occupy the same machine word and that
the compiler provides no locking?  So if one thread attempts to modify
"read_only" while another modifies "has_request", one can corrupt the work
of the other?

If this is indeed safe then a comment here whcih clearly explains that
would be useful.

> +	unsigned char         transfer_cmd;
> +
> +	int                   (*mrq_handler)(struct memstick_dev *card,
> +					     struct memstick_request **mrq);
> +
> +	unsigned char         attr_count;
> +	struct mspro_sys_attr *attributes;
> +
> +	struct scatterlist    req_sg[MSPRO_BLOCK_MAX_SEGS];
> +	unsigned int          seg_count;
> +	unsigned int          current_seg;
> +	unsigned short        current_page;
> +};
>
> ...
>
> +static ssize_t mspro_block_attr_show_default(struct device *dev,
> +					     struct device_attribute *attr,
> +					     char *buffer)
> +{
> +	struct mspro_sys_attr *x_attr = container_of(attr,
> +						     struct mspro_sys_attr,
> +						     sys_attr);
> +
> +	ssize_t cnt, rc = 0;
> +
> +	for (cnt = 0; cnt < x_attr->size; cnt++) {
> +		if (cnt && !(cnt % 16)) {
> +			if (PAGE_SIZE - rc)
> +				buffer[rc++] = '\n';
> +		}
> +
> +		rc += snprintf(buffer + rc, PAGE_SIZE - rc, "%02x ",
> +			       x_attr->data[cnt]);

scnprintf, I suspect?

> +	}
> +	return rc;
> +}
> +
> +static ssize_t mspro_block_attr_show_sysinfo(struct device *dev,
> +					     struct device_attribute *attr,
> +					     char *buffer)
> +{
> +	struct mspro_sys_attr *x_attr = container_of(attr,
> +						     struct mspro_sys_attr,
> +						     sys_attr);
> +	struct mspro_sys_info *x_sys = (struct mspro_sys_info *)x_attr->data;

Maybe .data should have been void*.

> +	ssize_t rc = 0;
> +
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "class: %x\n",
> +		       x_sys->class);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "block size: %x\n",
> +		       be16_to_cpu(x_sys->block_size));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "block count: %x\n",
> +		       be16_to_cpu(x_sys->block_count));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "user block count: %x\n",
> +		       be16_to_cpu(x_sys->user_block_count));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "page size: %x\n",
> +		       be16_to_cpu(x_sys->page_size));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "assembly date: "
> +		       "%d %04u-%02u-%02u %02u:%02u:%02u\n",
> +		       x_sys->assembly_date[0],
> +		       be16_to_cpu(*(unsigned short *)&x_sys->assembly_date[1]),
> +		       x_sys->assembly_date[3], x_sys->assembly_date[4],
> +		       x_sys->assembly_date[5], x_sys->assembly_date[6],
> +		       x_sys->assembly_date[7]);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "serial number: %x\n",
> +		       be32_to_cpu(x_sys->serial_number));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "assembly maker code: %x\n",
> +		       x_sys->assembly_maker_code);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "assembly model code: "
> +		       "%02x%02x%02x\n", x_sys->assembly_model_code[0],
> +		       x_sys->assembly_model_code[1],
> +		       x_sys->assembly_model_code[2]);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "memory maker code: %x\n",
> +		       be16_to_cpu(x_sys->memory_maker_code));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "memory model code: %x\n",
> +		       be16_to_cpu(x_sys->memory_model_code));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "vcc: %x\n",
> +		       x_sys->vcc);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "vpp: %x\n",
> +		       x_sys->vpp);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "controller number: %x\n",
> +		       be16_to_cpu(x_sys->controller_number));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "controller function: %x\n",
> +		       be16_to_cpu(x_sys->controller_function));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n",
> +		       be16_to_cpu(x_sys->start_sector));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "unit size: %x\n",
> +		       be16_to_cpu(x_sys->unit_size));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "sub class: %x\n",
> +		       x_sys->ms_sub_class);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "interface type: %x\n",
> +		       x_sys->interface_type);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "controller code: %x\n",
> +		       be16_to_cpu(x_sys->controller_code));
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "format type: %x\n",
> +		       x_sys->format_type);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "device type: %x\n",
> +		       x_sys->device_type);
> +	rc += snprintf(buffer + rc, PAGE_SIZE - rc, "mspro id: %s\n",
> +		       x_sys->mspro_id);
> +	return rc;

Again, this will return a wrong result if the output was truncated. 
scnprintf would fix.

> +}
> +
> +static ssize_t mspro_block_attr_show_modelname(struct device *dev,
> +					       struct device_attribute *attr,
> +					       char *buffer)
> +{
> +	struct mspro_sys_attr *x_attr = container_of(attr,
> +						     struct mspro_sys_attr,
> +						     sys_attr);
> +
> +	return snprintf(buffer, PAGE_SIZE, "%s", x_attr->data);
> +}
>
> ...
>
> +static int mspro_block_sysfs_register(struct memstick_dev *card)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	int cnt, rc = 0;
> +
> +	for (cnt = 0; cnt < msb->attr_count; cnt++) {
> +		rc = device_create_file(&card->dev,
> +					&msb->attributes[cnt].sys_attr);
> +
> +		if (rc) {
> +			if (cnt) {
> +				for (cnt--; cnt >= 0; cnt--)

ow. my brain hurts.

The `if (cnt)' is actually unneeded.  But if it were mine I'd be doing
something simpler and obviouser here.  Like `for (i = 0; i < cnt; i++)'
simple == good.  !simple == !.

> +					device_remove_file(&card->dev,
> +							   &msb->attributes[cnt]
> +								.sys_attr);
> +			}
> +			break;
> +		}
> +	}
> +	return rc;
> +}

Could we be using attribute groups for all this?

> +static void mspro_block_sysfs_unregister(struct memstick_dev *card)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	int cnt;
> +
> +	for (cnt = 0; cnt < msb->attr_count; cnt++)
> +		device_remove_file(&card->dev, &msb->attributes[cnt].sys_attr);
> +}
>
> ...
>
> +static int h_mspro_block_get_ro(struct memstick_dev *card,
> +				struct memstick_request **mrq)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +
> +	if ((*mrq)->error) {
> +		complete(&card->mrq_complete);
> +		return (*mrq)->error;
> +	}
> +
> +	if ((*mrq)->data[offsetof(struct ms_status_register, status0)]

It would be simpler and clearer to do

	struct ms_status_register *msr;
	msr = (struct ms_status_register *)(*mrq)->data;
	if (msr->status0)

no?

Again, making .data void* would improve things here.

> +	    & MEMSTICK_STATUS0_WP)
> +		msb->read_only = 1;
> +	else
> +		msb->read_only = 0;
> +
> +	complete(&card->mrq_complete);
> +	return -EAGAIN;
> +}
> +
> +static int h_mspro_block_wait_for_ced(struct memstick_dev *card,
> +				      struct memstick_request **mrq)
> +{
> +	if ((*mrq)->error) {
> +		complete(&card->mrq_complete);
> +		return (*mrq)->error;
> +	}
> +
> +	dev_dbg(&card->dev, "wait for ced: value %x\n", (*mrq)->data[0]);
> +
> +	if ((*mrq)->data[0] & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {

elsewhere.

> +		card->current_mrq.error = -EFAULT;
> +		complete(&card->mrq_complete);
> +		return card->current_mrq.error;
> +	}
> +
> +	if (!((*mrq)->data[0] & MEMSTICK_INT_CED))
> +		return 0;
> +	else {
> +		card->current_mrq.error = 0;
> +		complete(&card->mrq_complete);
> +		return -EAGAIN;
> +	}
> +}
> +
> +static int h_mspro_block_transfer_data(struct memstick_dev *card,
> +				       struct memstick_request **mrq)
> +{
> +	struct memstick_host *host = card->host;
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	unsigned char t_val = 0;
> +	struct scatterlist t_sg = { 0 };

Should this be using sg_init_table()?

> +	size_t t_offset;
> +
> +	if ((*mrq)->error) {
> +		complete(&card->mrq_complete);
> +		return (*mrq)->error;
> +	}
> +
> +	switch ((*mrq)->tpc) {
> +	case MS_TPC_WRITE_REG:
> +		memstick_init_req(*mrq, MS_TPC_SET_CMD, &msb->transfer_cmd, 1);
> +		(*mrq)->get_int_reg = 1;
> +		return 0;
> +	case MS_TPC_SET_CMD:
> +		t_val = (*mrq)->int_reg;
> +		memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
> +		if (host->caps & MEMSTICK_CAP_AUTO_GET_INT)
> +			goto has_int_reg;
> +		return 0;
>
> ...
>
> +
> +static void mspro_block_process_request(struct memstick_dev *card,
> +					struct request *req)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	struct mspro_param_register param;
> +	int rc, chunk, cnt;
> +	unsigned short page_count;
> +	sector_t t_sec;
> +	unsigned long flags;
> +
> +	do {
> +		page_count = 0;
> +		msb->current_seg = 0;
> +		msb->seg_count = blk_rq_map_sg(req->q, req, msb->req_sg);
> +
> +		if (msb->seg_count) {
> +			msb->current_page = 0;
> +			for (rc = 0; rc < msb->seg_count; rc++)
> +				page_count += msb->req_sg[rc].length
> +					      / msb->page_size;
> +
> +			t_sec = req->sector;
> +			sector_div(t_sec, msb->page_size >> 9);
> +			param = (struct mspro_param_register) {
> +				.system = msb->system,
> +				.data_count = cpu_to_be16(page_count),
> +				.data_address = cpu_to_be32((uint32_t)t_sec),
> +				.cmd_param = 0
> +			};

Might generate bad code.

> +			msb->data_dir = rq_data_dir(req);
> +			msb->transfer_cmd = msb->data_dir == READ
> +					    ? MSPRO_CMD_READ_DATA
> +					    : MSPRO_CMD_WRITE_DATA;
> +
> +			dev_dbg(&card->dev, "data transfer: cmd %x, "
> +				"lba %x, count %x\n", msb->transfer_cmd,
> +				be32_to_cpu(param.data_address),
> +				page_count);
> +
> +			card->next_request = h_mspro_block_req_init;
> +			msb->mrq_handler = h_mspro_block_transfer_data;
> +			memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
> +					  &param, sizeof(param));
> +			memstick_new_req(card->host);
> +			wait_for_completion(&card->mrq_complete);
> +			rc = card->current_mrq.error;
> +
> +			if (rc || (card->current_mrq.tpc == MSPRO_CMD_STOP)) {
> +				for (cnt = 0; cnt < msb->current_seg; cnt++)
> +					page_count += msb->req_sg[cnt].length
> +						      / msb->page_size;
> +
> +				if (msb->current_page)
> +					page_count += msb->current_page - 1;
> +
> +				if (page_count && (msb->data_dir == READ))
> +					rc = msb->page_size * page_count;
> +				else
> +					rc = -EIO;
> +			} else
> +				rc = msb->page_size * page_count;
> +		} else
> +			rc = -EFAULT;
> +
> +		spin_lock_irqsave(&msb->q_lock, flags);
> +		if (rc >= 0)
> +			chunk = end_that_request_chunk(req, 1, rc);
> +		else
> +			chunk = end_that_request_first(req, rc,
> +						       req->current_nr_sectors);
> +
> +		dev_dbg(&card->dev, "end chunk %d, %d\n", rc, chunk);
> +		if (!chunk) {
> +			add_disk_randomness(req->rq_disk);
> +			blkdev_dequeue_request(req);
> +			end_that_request_last(req, rc > 0 ? 1 : rc);
> +		}
> +		spin_unlock_irqrestore(&msb->q_lock, flags);
> +	} while (chunk);
> +
> +}
>
> ...
>
>
> +static int mspro_block_queue_thread(void *data)
> +{
> +	struct memstick_dev *card = data;
> +	struct memstick_host *host = card->host;
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	struct request *req;
> +	unsigned long flags;
> +
> +	while (1) {
> +		wait_event(msb->q_wait, mspro_block_has_request(msb));
> +		dev_dbg(&card->dev, "thread iter\n");
> +
> +		spin_lock_irqsave(&msb->q_lock, flags);
> +		req = elv_next_request(msb->queue);
> +		dev_dbg(&card->dev, "next req %p\n", req);
> +		if (!req) {
> +			msb->has_request = 0;
> +			if (kthread_should_stop()) {
> +				spin_unlock_irqrestore(&msb->q_lock, flags);
> +				break;
> +			}
> +		} else
> +			msb->has_request = 1;
> +		spin_unlock_irqrestore(&msb->q_lock, flags);
> +
> +		if (req) {
> +			mutex_lock(&host->lock);
> +			mspro_block_process_request(card, req);
> +			mutex_unlock(&host->lock);
> +		}
> +	}

Should this have a try_to_freeze() in it?

> +	dev_dbg(&card->dev, "thread finished\n");
> +	return 0;
> +}
> +
> +static void mspro_block_request(struct request_queue *q)
> +{
> +	struct memstick_dev *card = q->queuedata;
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	struct request *req = NULL;
> +
> +	if (!msb->q_thread) {
> +		for (req = elv_next_request(q); req;
> +		     req = elv_next_request(q)) {
> +			while (end_that_request_chunk(req, -ENODEV,
> +						      req->current_nr_sectors
> +						      << 9)) {}
> +			end_that_request_last(req, -ENODEV);
> +		}
> +	} else {
> +		msb->has_request = 1;
> +		wake_up_all(&msb->q_wait);
> +	}
> +}

Suggest that you cc Jens on this, see if he can check it all over.

> +/*** Initialization ***/
> +
> +static int mspro_block_wait_for_ced(struct memstick_dev *card)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +
> +	card->next_request = h_mspro_block_req_init;
> +	msb->mrq_handler = h_mspro_block_wait_for_ced;
> +	memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
> +	memstick_new_req(card->host);
> +	wait_for_completion(&card->mrq_complete);
> +	return card->current_mrq.error;
> +}
>
> ...
>
> +static int mspro_block_read_attributes(struct memstick_dev *card)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	struct mspro_param_register param = {
> +		.system = msb->system,
> +		.data_count = cpu_to_be16(1),
> +		.data_address = 0,
> +		.cmd_param = 0
> +	};
> +	struct mspro_attribute *attr = NULL;
> +	unsigned char *buffer = NULL;
> +	int cnt, rc;
> +	unsigned int addr;
> +	unsigned short page_count;
> +
> +	attr = kmalloc(msb->page_size, GFP_KERNEL);
> +	if (!attr)
> +		return -ENOMEM;
> +
> +	sg_init_one(&msb->req_sg[0], attr, msb->page_size);
> +	msb->seg_count = 1;
> +	msb->current_seg = 0;
> +	msb->current_page = 0;
> +	msb->data_dir = READ;
> +	msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
> +
> +	card->next_request = h_mspro_block_req_init;
> +	msb->mrq_handler = h_mspro_block_transfer_data;
> +	memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, &param,
> +			  sizeof(param));
> +	memstick_new_req(card->host);
> +	wait_for_completion(&card->mrq_complete);
> +	if (card->current_mrq.error) {
> +		rc = card->current_mrq.error;
> +		goto out_free_attr;
> +	}
> +
> +	if (be16_to_cpu(attr->signature) != MSPRO_BLOCK_SIGNATURE) {
> +		printk(KERN_ERR "%s: unrecognized device signature %x\n",
> +		       card->dev.bus_id, be16_to_cpu(attr->signature));
> +		rc = -ENODEV;
> +		goto out_free_attr;
> +	}
> +
> +	if (attr->count > MSPRO_BLOCK_MAX_ATTRIBUTES) {
> +		printk(KERN_WARNING "%s: way too many attribute entries\n",
> +		       card->dev.bus_id);
> +		msb->attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES;
> +	} else
> +		msb->attr_count = attr->count;
> +
> +	msb->attributes = kzalloc(msb->attr_count
> +				  * sizeof(struct mspro_sys_attr),
> +				  GFP_KERNEL);
> +	if (!msb->attributes) {
> +		msb->attr_count = 0;
> +		rc = -ENOMEM;
> +		goto out_free_attr;
> +	}
> +
> +	buffer = kmalloc(msb->page_size, GFP_KERNEL);
> +	if (!buffer) {
> +		rc = -ENOMEM;
> +		goto out_free_attr;

I think we leak msb->attributes if this is taken.  Please double-check the
unwinding here.

> +	}
> +	memcpy(buffer, (char *)attr, msb->page_size);

unneeded cast?

> +	page_count = 1;
> +
> +	for (cnt = 0; cnt < msb->attr_count; cnt++) {
> +		addr = be32_to_cpu(attr->entries[cnt].address);
> +		rc = be32_to_cpu(attr->entries[cnt].size);
> +		dev_dbg(&card->dev, "adding attribute %d: id %x, address %x, "
> +			"size %x\n", cnt, attr->entries[cnt].id, addr, rc);
> +		msb->attributes[cnt].id = attr->entries[cnt].id;
> +		if (mspro_block_attr_name(attr->entries[cnt].id))
> +			snprintf(msb->attributes[cnt].name,
> +				 sizeof(msb->attributes[cnt].name), "%s",
> +				 mspro_block_attr_name(attr->entries[cnt].id));
> +		else
> +			snprintf(msb->attributes[cnt].name,
> +				 sizeof(msb->attributes[cnt].name),
> +				 "attr_x%02x",
> +				 attr->entries[cnt].id);
> +
> +		msb->attributes[cnt].sys_attr
> +			= (struct device_attribute){
> +				.attr = {
> +					.name = msb->attributes[cnt].name,
> +					.mode = S_IRUGO,
> +					.owner = THIS_MODULE
> +				},
> +				.show = mspro_block_attr_show(
> +						msb->attributes[cnt].id),
> +				.store = NULL
> +			};

Wow, that's giving the compiler a workout.  Trusting soul ;)

Alas, you may fnd the result isn't good.

> +		if (!rc)
> +			continue;
> +
> +		msb->attributes[cnt].size = rc;
> +		msb->attributes[cnt].data = kmalloc(rc, GFP_KERNEL);
> +		if (!msb->attributes[cnt].data) {
> +			rc = -ENOMEM;
> +			goto out_free_buffer;
> +		}
> +
> +		if (((addr / msb->page_size)
> +		     == be32_to_cpu(param.data_address))
> +		    && (((addr + rc - 1) / msb->page_size)
> +			== be32_to_cpu(param.data_address))) {
> +			memcpy(msb->attributes[cnt].data,
> +			       buffer + addr % msb->page_size,
> +			       rc);
> +			continue;
> +		}
> +
> +		if (page_count <= (rc / msb->page_size)) {
> +			kfree(buffer);
> +			page_count = (rc / msb->page_size) + 1;
> +			buffer = kmalloc(page_count * msb->page_size,
> +					 GFP_KERNEL);
> +			if (!buffer) {
> +				rc = -ENOMEM;
> +				goto out_free_attr;
> +			}
> +		}
> +
> +		param = (struct mspro_param_register){
> +			.system = msb->system,
> +			.data_count = cpu_to_be16((rc / msb->page_size) + 1),
> +			.data_address = cpu_to_be32(addr / msb->page_size),
> +			.cmd_param = 0
> +		};
> +
> +		sg_init_one(&msb->req_sg[0], buffer,
> +			    be16_to_cpu(param.data_count) * msb->page_size);
> +		msb->seg_count = 1;
> +		msb->current_seg = 0;
> +		msb->current_page = 0;
> +		msb->data_dir = READ;
> +		msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
> +
> +		dev_dbg(&card->dev, "reading attribute pages %x, %x\n",
> +			be32_to_cpu(param.data_address),
> +			be16_to_cpu(param.data_count));
> +
> +		card->next_request = h_mspro_block_req_init;
> +		msb->mrq_handler = h_mspro_block_transfer_data;
> +		memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
> +				  (char *)&param, sizeof(param));
> +		memstick_new_req(card->host);
> +		wait_for_completion(&card->mrq_complete);
> +		if (card->current_mrq.error) {
> +			rc = card->current_mrq.error;
> +			goto out_free_buffer;
> +		}
> +
> +		memcpy(msb->attributes[cnt].data,
> +		       buffer + addr % msb->page_size,
> +		       rc);
> +	}
> +
> +	rc = 0;
> +out_free_buffer:
> +	kfree(buffer);
> +out_free_attr:
> +	kfree(attr);
> +	return rc;
> +}
>
> ...
>
> +static int mspro_block_resume(struct memstick_dev *card)
> +{
> +	struct mspro_block_data *msb = memstick_get_drvdata(card);
> +	unsigned long flags;
> +	int rc = 0;
> +
> +#ifdef CONFIG_MEMSTICK_UNSAFE_RESUME
> +
> +	struct mspro_block_data *new_msb;
> +	struct memstick_host *host = card->host;
> +	unsigned char cnt;
> +
> +	mutex_lock(&host->lock);
> +	new_msb = kzalloc(sizeof(struct mspro_block_data), GFP_KERNEL);

If anything takes host->lock on the IO path then there might be a deadlock
here.  GFP_NOIO, perhaps.  or just swap these two lines.

> +	if (!new_msb) {
> +		rc = -ENOMEM;
> +		goto out_unlock;
> +	}
> +
> +	new_msb->card = card;
> +	memstick_set_drvdata(card, new_msb);
> +	if (mspro_block_init_card(card))

except this might do allocations too, dunno.

> +		goto out_free;
> +
> +	for (cnt = 0; cnt < new_msb->attr_count; cnt++) {
> +		if (new_msb->attributes[cnt].id == MSPRO_BLOCK_ID_SYSINFO
> +		    && cnt < msb->attr_count
> +		    && msb->attributes[cnt].id == MSPRO_BLOCK_ID_SYSINFO) {
> +			if (memcmp(new_msb->attributes[cnt].data,
> +				   msb->attributes[cnt].data,
> +				   msb->attributes[cnt].size))
> +				break;
> +
> +			memstick_set_drvdata(card, msb);
> +			msb->q_thread = kthread_run(mspro_block_queue_thread,
> +						    card, DRIVER_NAME"d");
> +			if (IS_ERR(msb->q_thread))
> +				msb->q_thread = NULL;
> +			else
> +				msb->active = 1;
> +
> +			break;
> +		}
> +	}
> +
> +out_free:
> +	memstick_set_drvdata(card, msb);
> +	mspro_block_data_clear(new_msb);
> +	kfree(new_msb);
> +out_unlock:
> +	mutex_unlock(&host->lock);
> +
> +#endif /* CONFIG_MEMSTICK_UNSAFE_RESUME */
> +
> +	spin_lock_irqsave(&msb->q_lock, flags);
> +	blk_start_queue(msb->queue);
> +	spin_unlock_irqrestore(&msb->q_lock, flags);
> +	return rc;
> +}

Here my attention span ran out.  Except for...

> +inline void *memstick_priv(struct memstick_host *host)
> +{
> +	return (void *)host->private;
> +}
> +
> +inline void *memstick_get_drvdata(struct memstick_dev *card)
> +{
> +	return dev_get_drvdata(&card->dev);
> +}
> +
> +inline void memstick_set_drvdata(struct memstick_dev *card, void *data)
> +{
> +	dev_set_drvdata(&card->dev, data);
> +}

static inline.


Generally: a clean and idiomatic-looking driver but I am not able to review
it very effectively due to its extraordinary lack of commentary.


  reply	other threads:[~2008-01-10  9:01 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-01-02  6:42 oakad
2008-01-10  9:00 ` Andrew Morton [this message]
2008-01-11 12:14   ` Jens Axboe
2008-01-14  3:26   ` Alex Dubov
2008-01-22 16:12   ` Alex Dubov
2008-01-22 18:59     ` Andrew Morton
2008-01-25  7:58       ` [PATCH] [MEMSTICK] Updates for the memstick driver Alex Dubov
2008-01-27  6:01         ` Andrew Morton
2008-02-03  0:16         ` Andrew Morton
2008-02-04  4:31           ` [PATCH] memstick: use __blk_end_request to complete requests Alex Dubov
2008-02-04  7:07             ` Andrew Morton
2008-02-09 14:59               ` [PATCH] memstick: fix attribute structure casting in mspro_block_resume Alex Dubov
2008-01-15 17:21 ` [PATCH] [MEMSTICK] Initial commit for Sony MemoryStick support Mariusz Kozlowski
2008-01-16  1:52   ` Alex Dubov
  -- strict thread matches above, loose matches on Subject: below --
2008-01-13 22:47 Miguel Botón
2007-12-24  3:06 oakad
2007-12-31  0:01 ` Carlos Corbacho
2007-12-31  0:31   ` Jan Engelhardt
2007-12-31  0:54     ` Alex Dubov
2007-12-31  0:56     ` Carlos Corbacho
2007-12-31  1:01   ` Alex Dubov
2007-12-31  1:15     ` Carlos Corbacho
2007-12-31  4:51       ` Alex Dubov

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=20080110010049.695da076.akpm@linux-foundation.org \
    --to=akpm@linux-foundation.org \
    --cc=jens.axboe@oracle.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=oakad@exemail.com.au \
    --cc=oakad@yahoo.com \
    --subject='Re: [PATCH] [MEMSTICK] Initial commit for Sony MemoryStick support' \
    /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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).