Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b04efa93 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: f_mass_storage: Add timer to catch VFS read/write/sync stuck issues"

parents 707da751 86143d8d
Loading
Loading
Loading
Loading
+70 −2
Original line number Diff line number Diff line
@@ -251,6 +251,16 @@ static struct usb_gadget_strings *fsg_strings_array[] = {

/*-------------------------------------------------------------------------*/

/*
 * If USB mass storage vfs operation is stuck for more than 10 sec
 * host will initiate the reset. Configure the timer with 9 sec to print
 * the error message before host is intiating the resume on it.
 */
#define MSC_VFS_TIMER_PERIOD_MS	9000
static int msc_vfs_timer_period_ms = MSC_VFS_TIMER_PERIOD_MS;
module_param(msc_vfs_timer_period_ms, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(msc_vfs_timer_period_ms, "Set period for MSC VFS timer");

struct fsg_dev;
struct fsg_common;

@@ -316,6 +326,7 @@ struct fsg_common {
	/* LUN name for sysfs purpose */
	char name[FSG_MAX_LUNS][LUN_NAME_LEN];
	struct kref		ref;
	struct timer_list	vfs_timer;
};

struct fsg_dev {
@@ -335,6 +346,26 @@ struct fsg_dev {
	struct usb_ep		*bulk_out;
};

static void msc_usb_vfs_timer_func(unsigned long data)
{
	struct fsg_common *common = (struct fsg_common *) data;

	switch (common->data_dir) {
	case DATA_DIR_FROM_HOST:
		dev_err(&common->curlun->dev,
				"usb mass storage stuck in vfs_write\n");
		break;
	case DATA_DIR_TO_HOST:
		dev_err(&common->curlun->dev,
				"usb mass storage stuck in vfs_read\n");
		break;
	default:
		dev_err(&common->curlun->dev,
				"usb mass storage stuck in vfs_sync\n");
		break;
	}
}

static inline int __fsg_is_set(struct fsg_common *common,
			       const char *func, unsigned line)
{
@@ -635,7 +666,7 @@ static int do_read(struct fsg_common *common)
	loff_t			file_offset, file_offset_tmp;
	unsigned int		amount;
	ssize_t			nread;

	ktime_t			start, diff;
	/*
	 * Get the starting Logical Block Address and check that it's
	 * not too big.
@@ -702,11 +733,18 @@ static int do_read(struct fsg_common *common)

		/* Perform the read */
		file_offset_tmp = file_offset;
		start = ktime_get();
		mod_timer(&common->vfs_timer, jiffies +
			msecs_to_jiffies(msc_vfs_timer_period_ms));
		nread = vfs_read(curlun->filp,
				 (char __user *)bh->buf,
				 amount, &file_offset_tmp);
		del_timer_sync(&common->vfs_timer);
		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
		      (unsigned long long)file_offset, (int)nread);
		diff = ktime_sub(ktime_get(), start);
		curlun->perf.rbytes += nread;
		curlun->perf.rtime = ktime_add(curlun->perf.rtime, diff);
		if (signal_pending(current))
			return -EINTR;

@@ -767,6 +805,7 @@ static int do_write(struct fsg_common *common)
	unsigned int		amount;
	ssize_t			nwritten;
	int			rc;
	ktime_t			start, diff;

	if (curlun->ro) {
		curlun->sense_data = SS_WRITE_PROTECTED;
@@ -895,11 +934,19 @@ static int do_write(struct fsg_common *common)

			/* Perform the write */
			file_offset_tmp = file_offset;
			start = ktime_get();
			mod_timer(&common->vfs_timer, jiffies +
				msecs_to_jiffies(msc_vfs_timer_period_ms));
			nwritten = vfs_write(curlun->filp,
					     (char __user *)bh->buf,
					     amount, &file_offset_tmp);
			del_timer_sync(&common->vfs_timer);
			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
			      (unsigned long long)file_offset, (int)nwritten);
			diff = ktime_sub(ktime_get(), start);
			curlun->perf.wbytes += nwritten;
			curlun->perf.wtime =
					ktime_add(curlun->perf.wtime, diff);
			if (signal_pending(current))
				return -EINTR;		/* Interrupted! */

@@ -953,9 +1000,12 @@ static int do_synchronize_cache(struct fsg_common *common)

	/* We ignore the requested LBA and write out all file's
	 * dirty data buffers. */
	mod_timer(&common->vfs_timer, jiffies +
		msecs_to_jiffies(msc_vfs_timer_period_ms));
	rc = fsg_lun_fsync_sub(curlun);
	if (rc)
		curlun->sense_data = SS_WRITE_ERROR;
	del_timer_sync(&common->vfs_timer);
	return 0;
}

@@ -1011,7 +1061,10 @@ static int do_verify(struct fsg_common *common)
	file_offset = ((loff_t) lba) << curlun->blkbits;

	/* Write out all the dirty buffers before invalidating them */
	mod_timer(&common->vfs_timer, jiffies +
			msecs_to_jiffies(msc_vfs_timer_period_ms));
	fsg_lun_fsync_sub(curlun);
	del_timer_sync(&common->vfs_timer);
	if (signal_pending(current))
		return -EINTR;

@@ -1041,9 +1094,12 @@ static int do_verify(struct fsg_common *common)

		/* Perform the read */
		file_offset_tmp = file_offset;
		mod_timer(&common->vfs_timer, jiffies +
				msecs_to_jiffies(msc_vfs_timer_period_ms));
		nread = vfs_read(curlun->filp,
				(char __user *) bh->buf,
				amount, &file_offset_tmp);
		del_timer_sync(&common->vfs_timer);
		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
				(unsigned long long) file_offset,
				(int) nread);
@@ -1374,8 +1430,12 @@ static int do_prevent_allow(struct fsg_common *common)
		return -EINVAL;
	}

	if (curlun->prevent_medium_removal && !prevent)
	if (!curlun->nofua && curlun->prevent_medium_removal && !prevent) {
		mod_timer(&common->vfs_timer, jiffies +
			msecs_to_jiffies(msc_vfs_timer_period_ms));
		fsg_lun_fsync_sub(curlun);
		del_timer_sync(&common->vfs_timer);
	}
	curlun->prevent_medium_removal = prevent;
	return 0;
}
@@ -2641,6 +2701,7 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(ro);
static DEVICE_ATTR_RW(nofua);
static DEVICE_ATTR_RW(file);
static DEVICE_ATTR(perf, 0644, fsg_show_perf, fsg_store_perf);

static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro);
static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file);
@@ -2785,6 +2846,7 @@ static inline void fsg_common_remove_sysfs(struct fsg_lun *lun)
	 */
	device_remove_file(&lun->dev, &dev_attr_ro);
	device_remove_file(&lun->dev, &dev_attr_file);
	device_remove_file(&lun->dev, &dev_attr_perf);
}

void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
@@ -2918,6 +2980,10 @@ static inline int fsg_common_add_sysfs(struct fsg_common *common,
	if (rc)
		goto error;

	rc = device_create_file(&lun->dev, &dev_attr_perf);
	if (rc)
		pr_err("failed to create sysfs entry: %d\n", rc);

	return 0;

error:
@@ -3672,6 +3738,8 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
	fsg->function.free_func	= fsg_free;

	fsg->common               = common;
	setup_timer(&common->vfs_timer, msc_usb_vfs_timer_func,
		(unsigned long) common);

	return &fsg->function;
}
+30 −0
Original line number Diff line number Diff line
@@ -333,6 +333,36 @@ ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
}
EXPORT_SYMBOL_GPL(fsg_show_nofua);

ssize_t fsg_show_perf(struct device *dev, struct device_attribute *attr,
				char *buf)
{
	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
	unsigned long rbytes, wbytes;
	int64_t rtime, wtime;

	rbytes = curlun->perf.rbytes;
	wbytes = curlun->perf.wbytes;
	rtime = ktime_to_us(curlun->perf.rtime);
	wtime = ktime_to_us(curlun->perf.wtime);

	return snprintf(buf, PAGE_SIZE,
			"Write performance :%lu bytes in %lld microseconds\n"
			"Read performance : %lu bytes in %lld microseconds\n",
			wbytes, wtime, rbytes, rtime);
}

ssize_t fsg_store_perf(struct device *dev, struct device_attribute *attr,
				const char *buf, size_t count)
{
	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
	int value;

	if (!kstrtoint(buf, 10, &value) && !value)
		memset(&curlun->perf, 0, sizeof(curlun->perf));

	return count;
}

ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
		      char *buf)
{
+11 −0
Original line number Diff line number Diff line
@@ -110,6 +110,13 @@ struct fsg_lun {
						       of bound block device */
	unsigned int	blksize; /* logical block size of bound block device */
	struct device	dev;
	struct {
		unsigned long rbytes;
		unsigned long wbytes;
		ktime_t rtime;
		ktime_t wtime;
	} perf;

	const char	*name;		/* "lun.name" */
	const char	**name_pfx;	/* "function.name" */
};
@@ -227,5 +234,9 @@ ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
			const char *buf, size_t count);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
			    size_t count);
ssize_t fsg_show_perf(struct device *dev, struct device_attribute *attr,
				char *buf);
ssize_t fsg_store_perf(struct device *dev, struct device_attribute *attr,
				const char *buf, size_t count);

#endif /* USB_STORAGE_COMMON_H */