* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
@ 2020-05-10 22:14 kbuild test robot
0 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2020-05-10 22:14 UTC (permalink / raw)
To: kbuild
[-- Attachment #1: Type: text/plain, Size: 18554 bytes --]
CC: kbuild-all(a)lists.01.org
In-Reply-To: <20200510202436.63222-10-keescook@chromium.org>
References: <20200510202436.63222-10-keescook@chromium.org>
TO: Kees Cook <keescook@chromium.org>
Hi Kees,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on next-20200508]
[cannot apply to kees/for-next/pstore ia64/next linus/master v5.7-rc4 v5.7-rc3 v5.7-rc2 v5.7-rc4]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Kees-Cook/pstore-mtd-support-crash-log-to-block-and-mtd-device/20200511-043555
base: 30e2206e11ce27ae910cc0dab21472429e400a87
:::::: branch date: 2 hours ago
:::::: commit date: 2 hours ago
If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>
cppcheck warnings: (new ones prefixed by >>)
>> fs/pstore/ram.c:927:19: warning: Variable 'pdata.max_reason' is reassigned a value before the old one has been used. [redundantAssignment]
pdata.max_reason = ramoops_max_reason;
^
fs/pstore/ram.c:925:20: note: pdata.max_reason is assigned
pdata.max_reason = ramoops_dump_oops ? KMSG_DUMP_OOPS
^
fs/pstore/ram.c:927:19: note: pdata.max_reason is overwritten
pdata.max_reason = ramoops_max_reason;
^
--
>> fs/pstore/zone.c:433:6: warning: Redundant initialization for 'ret'. The initialized value is overwritten before it is read. [redundantInitialization]
ret = psz_kmsg_recover(cxt);
^
fs/pstore/zone.c:428:10: note: ret is initialized
int ret = -EBUSY;
^
fs/pstore/zone.c:433:6: note: ret is overwritten
ret = psz_kmsg_recover(cxt);
^
>> fs/pstore/zone.c:895:6: warning: Redundant initialization for 'err'. The initialized value is overwritten before it is read. [redundantInitialization]
err = psz_alloc_zones(cxt);
^
fs/pstore/zone.c:839:10: note: err is initialized
int err = -EINVAL;
^
fs/pstore/zone.c:895:6: note: err is overwritten
err = psz_alloc_zones(cxt);
^
>> fs/pstore/blk.c:193:7: warning: Redundant initialization for 'bdev'. The initialized value is overwritten before it is read. [redundantInitialization]
bdev = blkdev_get_by_path(blkdev, mode, holder);
^
fs/pstore/blk.c:178:28: note: bdev is initialized
struct block_device *bdev = ERR_PTR(-ENODEV);
^
fs/pstore/blk.c:193:7: note: bdev is overwritten
bdev = blkdev_get_by_path(blkdev, mode, holder);
^
>> fs/pstore/blk.c:359:6: warning: Redundant initialization for 'ret'. The initialized value is overwritten before it is read. [redundantInitialization]
ret = psblk_register_do(&dev);
^
fs/pstore/blk.c:321:10: note: ret is initialized
int ret = -ENODEV;
^
fs/pstore/blk.c:359:6: note: ret is overwritten
ret = psblk_register_do(&dev);
^
# https://github.com/0day-ci/linux/commit/1fe49524a73cc357622236b617f4ccf861dde190
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 1fe49524a73cc357622236b617f4ccf861dde190
vim +/bdev +193 fs/pstore/blk.c
1fe49524a73cc3 WeiXiong Liao 2020-05-10 164
1fe49524a73cc3 WeiXiong Liao 2020-05-10 165 /**
1fe49524a73cc3 WeiXiong Liao 2020-05-10 166 * psblk_get_bdev() - open block device
1fe49524a73cc3 WeiXiong Liao 2020-05-10 167 *
1fe49524a73cc3 WeiXiong Liao 2020-05-10 168 * @holder: Exclusive holder identifier
1fe49524a73cc3 WeiXiong Liao 2020-05-10 169 * @info: Information about bdev to fill in
1fe49524a73cc3 WeiXiong Liao 2020-05-10 170 *
1fe49524a73cc3 WeiXiong Liao 2020-05-10 171 * Return: pointer to block device on success and others on error.
1fe49524a73cc3 WeiXiong Liao 2020-05-10 172 *
1fe49524a73cc3 WeiXiong Liao 2020-05-10 173 * On success, the returned block_device has reference count of one.
1fe49524a73cc3 WeiXiong Liao 2020-05-10 174 */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 175 static struct block_device *psblk_get_bdev(void *holder,
1fe49524a73cc3 WeiXiong Liao 2020-05-10 176 struct bdev_info *info)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 177 {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 178 struct block_device *bdev = ERR_PTR(-ENODEV);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 179 fmode_t mode = FMODE_READ | FMODE_WRITE;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 180 sector_t nr_sects;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 181
1fe49524a73cc3 WeiXiong Liao 2020-05-10 182 if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
1fe49524a73cc3 WeiXiong Liao 2020-05-10 183 return ERR_PTR(-EINVAL);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 184
1fe49524a73cc3 WeiXiong Liao 2020-05-10 185 if (pstore_zone_info)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 186 return ERR_PTR(-EBUSY);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 187
1fe49524a73cc3 WeiXiong Liao 2020-05-10 188 if (!blkdev[0])
1fe49524a73cc3 WeiXiong Liao 2020-05-10 189 return ERR_PTR(-ENODEV);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 190
1fe49524a73cc3 WeiXiong Liao 2020-05-10 191 if (holder)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 192 mode |= FMODE_EXCL;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 @193 bdev = blkdev_get_by_path(blkdev, mode, holder);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 194 if (IS_ERR(bdev)) {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 195 dev_t devt;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 196
1fe49524a73cc3 WeiXiong Liao 2020-05-10 197 devt = name_to_dev_t(blkdev);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 198 if (devt == 0)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 199 return ERR_PTR(-ENODEV);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 200 bdev = blkdev_get_by_dev(devt, mode, holder);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 201 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 202
1fe49524a73cc3 WeiXiong Liao 2020-05-10 203 nr_sects = part_nr_sects_read(bdev->bd_part);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 204 if (!nr_sects) {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 205 pr_err("not enough space for '%s'\n", blkdev);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 206 blkdev_put(bdev, mode);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 207 return ERR_PTR(-ENOSPC);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 208 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 209
1fe49524a73cc3 WeiXiong Liao 2020-05-10 210 if (info) {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 211 info->devt = bdev->bd_dev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 212 info->nr_sects = nr_sects;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 213 info->start_sect = get_start_sect(bdev);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 214 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 215
1fe49524a73cc3 WeiXiong Liao 2020-05-10 216 return bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 217 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 218
1fe49524a73cc3 WeiXiong Liao 2020-05-10 219 static void psblk_put_bdev(struct block_device *bdev, void *holder)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 220 {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 221 fmode_t mode = FMODE_READ | FMODE_WRITE;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 222
1fe49524a73cc3 WeiXiong Liao 2020-05-10 223 if (!bdev)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 224 return;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 225
1fe49524a73cc3 WeiXiong Liao 2020-05-10 226 if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
1fe49524a73cc3 WeiXiong Liao 2020-05-10 227 return;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 228
1fe49524a73cc3 WeiXiong Liao 2020-05-10 229 if (holder)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 230 mode |= FMODE_EXCL;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 231 blkdev_put(bdev, mode);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 232 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 233
1fe49524a73cc3 WeiXiong Liao 2020-05-10 234 static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 235 {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 236 struct block_device *bdev = psblk_bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 237 struct file file;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 238 struct kiocb kiocb;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 239 struct iov_iter iter;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 240 struct kvec iov = {.iov_base = buf, .iov_len = bytes};
1fe49524a73cc3 WeiXiong Liao 2020-05-10 241
1fe49524a73cc3 WeiXiong Liao 2020-05-10 242 if (!bdev)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 243 return -ENODEV;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 244
1fe49524a73cc3 WeiXiong Liao 2020-05-10 245 memset(&file, 0, sizeof(struct file));
1fe49524a73cc3 WeiXiong Liao 2020-05-10 246 file.f_mapping = bdev->bd_inode->i_mapping;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 247 file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 248 file.f_inode = bdev->bd_inode;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 249 file_ra_state_init(&file.f_ra, file.f_mapping);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 250
1fe49524a73cc3 WeiXiong Liao 2020-05-10 251 init_sync_kiocb(&kiocb, &file);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 252 kiocb.ki_pos = pos;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 253 iov_iter_kvec(&iter, READ, &iov, 1, bytes);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 254
1fe49524a73cc3 WeiXiong Liao 2020-05-10 255 return generic_file_read_iter(&kiocb, &iter);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 256 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 257
1fe49524a73cc3 WeiXiong Liao 2020-05-10 258 static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
1fe49524a73cc3 WeiXiong Liao 2020-05-10 259 loff_t pos)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 260 {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 261 struct block_device *bdev = psblk_bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 262 struct iov_iter iter;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 263 struct kiocb kiocb;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 264 struct file file;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 265 ssize_t ret;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 266 struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
1fe49524a73cc3 WeiXiong Liao 2020-05-10 267
1fe49524a73cc3 WeiXiong Liao 2020-05-10 268 if (!bdev)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 269 return -ENODEV;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 270
1fe49524a73cc3 WeiXiong Liao 2020-05-10 271 /* Console/Ftrace backend may handle buffer until flush dirty zones */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 272 if (in_interrupt() || irqs_disabled())
1fe49524a73cc3 WeiXiong Liao 2020-05-10 273 return -EBUSY;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 274
1fe49524a73cc3 WeiXiong Liao 2020-05-10 275 memset(&file, 0, sizeof(struct file));
1fe49524a73cc3 WeiXiong Liao 2020-05-10 276 file.f_mapping = bdev->bd_inode->i_mapping;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 277 file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 278 file.f_inode = bdev->bd_inode;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 279
1fe49524a73cc3 WeiXiong Liao 2020-05-10 280 init_sync_kiocb(&kiocb, &file);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 281 kiocb.ki_pos = pos;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 282 iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 283
1fe49524a73cc3 WeiXiong Liao 2020-05-10 284 inode_lock(bdev->bd_inode);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 285 ret = generic_write_checks(&kiocb, &iter);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 286 if (ret > 0)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 287 ret = generic_perform_write(&file, &iter, pos);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 288 inode_unlock(bdev->bd_inode);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 289
1fe49524a73cc3 WeiXiong Liao 2020-05-10 290 if (likely(ret > 0)) {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 291 const struct file_operations f_op = {.fsync = blkdev_fsync};
1fe49524a73cc3 WeiXiong Liao 2020-05-10 292
1fe49524a73cc3 WeiXiong Liao 2020-05-10 293 file.f_op = &f_op;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 294 kiocb.ki_pos += ret;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 295 ret = generic_write_sync(&kiocb, ret);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 296 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 297 return ret;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 298 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 299
1fe49524a73cc3 WeiXiong Liao 2020-05-10 300 static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
1fe49524a73cc3 WeiXiong Liao 2020-05-10 301 loff_t off)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 302 {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 303 int ret;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 304
1fe49524a73cc3 WeiXiong Liao 2020-05-10 305 if (!blkdev_panic_write)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 306 return -EOPNOTSUPP;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 307
1fe49524a73cc3 WeiXiong Liao 2020-05-10 308 /* size and off must align to SECTOR_SIZE for block device */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 309 ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
1fe49524a73cc3 WeiXiong Liao 2020-05-10 310 size >> SECTOR_SHIFT);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 311 return ret ? -EIO : size;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 312 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 313
1fe49524a73cc3 WeiXiong Liao 2020-05-10 314 static int __register_pstore_blk(struct pstore_blk_info *info)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 315 {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 316 char bdev_name[BDEVNAME_SIZE];
1fe49524a73cc3 WeiXiong Liao 2020-05-10 317 struct block_device *bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 318 struct pstore_device_info dev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 319 struct bdev_info binfo;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 320 void *holder = blkdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 321 int ret = -ENODEV;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 322
1fe49524a73cc3 WeiXiong Liao 2020-05-10 323 if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
1fe49524a73cc3 WeiXiong Liao 2020-05-10 324 return -EINVAL;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 325
1fe49524a73cc3 WeiXiong Liao 2020-05-10 326 /* hold bdev exclusively */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 327 memset(&binfo, 0, sizeof(binfo));
1fe49524a73cc3 WeiXiong Liao 2020-05-10 328 bdev = psblk_get_bdev(holder, &binfo);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 329 if (IS_ERR(bdev)) {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 330 pr_err("failed to open '%s'!\n", blkdev);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 331 ret = PTR_ERR(bdev);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 332 goto err_put_bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 333 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 334
1fe49524a73cc3 WeiXiong Liao 2020-05-10 335 /* only allow driver matching the @blkdev */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 336 if (!binfo.devt || MAJOR(binfo.devt) != info->major) {
1fe49524a73cc3 WeiXiong Liao 2020-05-10 337 pr_debug("invalid major %u (expect %u)\n",
1fe49524a73cc3 WeiXiong Liao 2020-05-10 338 info->major, MAJOR(binfo.devt));
1fe49524a73cc3 WeiXiong Liao 2020-05-10 339 ret = -ENODEV;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 340 goto err_put_bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 341 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 342
1fe49524a73cc3 WeiXiong Liao 2020-05-10 343 /* psblk_bdev must be assigned before register to pstore/blk */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 344 psblk_bdev = bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 345 blkdev_panic_write = info->panic_write;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 346
1fe49524a73cc3 WeiXiong Liao 2020-05-10 347 /* Copy back block device details. */
1fe49524a73cc3 WeiXiong Liao 2020-05-10 348 info->devt = binfo.devt;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 349 info->nr_sects = binfo.nr_sects;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 350 info->start_sect = binfo.start_sect;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 351
1fe49524a73cc3 WeiXiong Liao 2020-05-10 352 memset(&dev, 0, sizeof(dev));
1fe49524a73cc3 WeiXiong Liao 2020-05-10 353 dev.total_size = info->nr_sects << SECTOR_SHIFT;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 354 dev.flags = info->flags;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 355 dev.read = psblk_generic_blk_read;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 356 dev.write = psblk_generic_blk_write;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 357 dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 358
1fe49524a73cc3 WeiXiong Liao 2020-05-10 @359 ret = psblk_register_do(&dev);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 360 if (ret)
1fe49524a73cc3 WeiXiong Liao 2020-05-10 361 goto err_put_bdev;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 362
1fe49524a73cc3 WeiXiong Liao 2020-05-10 363 bdevname(bdev, bdev_name);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 364 pr_info("attached %s%s\n", bdev_name,
1fe49524a73cc3 WeiXiong Liao 2020-05-10 365 info->panic_write ? "" : " (no dedicated panic_write!)");
1fe49524a73cc3 WeiXiong Liao 2020-05-10 366 return 0;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 367
1fe49524a73cc3 WeiXiong Liao 2020-05-10 368 err_put_bdev:
1fe49524a73cc3 WeiXiong Liao 2020-05-10 369 psblk_bdev = NULL;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 370 blkdev_panic_write = NULL;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 371 psblk_put_bdev(bdev, holder);
1fe49524a73cc3 WeiXiong Liao 2020-05-10 372 return ret;
1fe49524a73cc3 WeiXiong Liao 2020-05-10 373 }
1fe49524a73cc3 WeiXiong Liao 2020-05-10 374
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
2020-05-11 15:36 ` Randy Dunlap
@ 2020-05-11 23:11 ` Kees Cook
-1 siblings, 0 replies; 11+ messages in thread
From: Kees Cook @ 2020-05-11 23:11 UTC (permalink / raw)
To: Randy Dunlap
Cc: WeiXiong Liao, Anton Vorontsov, Colin Cross, Tony Luck,
Jonathan Corbet, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Petr Mladek, Sergey Senozhatsky,
Steven Rostedt, Rob Herring, Pavel Tatashin, linux-doc,
linux-kernel, linux-mtd
On Mon, May 11, 2020 at 08:36:49AM -0700, Randy Dunlap wrote:
> On 5/10/20 1:24 PM, Kees Cook wrote:
> > diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> > index 98d2457bdd9f..92ba73bd0b62 100644
> > --- a/fs/pstore/Kconfig
> > +++ b/fs/pstore/Kconfig
> > @@ -160,3 +160,67 @@ config PSTORE_ZONE
> > help
> > The common layer for pstore/blk (and pstore/ram in the future)
> > to manage storage in zones.
> > +
> > +config PSTORE_BLK
> > + tristate "Log panic/oops to a block device"
> > + depends on PSTORE
> > + depends on BLOCK
> > + select PSTORE_ZONE
> > + default n
> > + help
> > + This enables panic and oops message to be logged to a block dev
> > + where it can be read back at some later point.
> > +
> > + If unsure, say N.
> > +
> > +config PSTORE_BLK_BLKDEV
> > + string "block device identifier"
> > + depends on PSTORE_BLK
> > + default ""
> > + help
> > + Which block device should be used for pstore/blk.
> > +
> > + It accept the following variants:
> > + 1) <hex_major><hex_minor> device number in hexadecimal represents
> > + itself no leading 0x, for example b302.
>
> itself with no leading 0x,
Yes, I've reworked the language here. Thanks!
--
Kees Cook
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
@ 2020-05-11 23:11 ` Kees Cook
0 siblings, 0 replies; 11+ messages in thread
From: Kees Cook @ 2020-05-11 23:11 UTC (permalink / raw)
To: Randy Dunlap
Cc: Petr Mladek, Tony Luck, Vignesh Raghavendra, Jonathan Corbet,
Richard Weinberger, Anton Vorontsov, linux-doc, linux-kernel,
Steven Rostedt, WeiXiong Liao, Sergey Senozhatsky, Colin Cross,
linux-mtd, Miquel Raynal, Rob Herring, Pavel Tatashin
On Mon, May 11, 2020 at 08:36:49AM -0700, Randy Dunlap wrote:
> On 5/10/20 1:24 PM, Kees Cook wrote:
> > diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> > index 98d2457bdd9f..92ba73bd0b62 100644
> > --- a/fs/pstore/Kconfig
> > +++ b/fs/pstore/Kconfig
> > @@ -160,3 +160,67 @@ config PSTORE_ZONE
> > help
> > The common layer for pstore/blk (and pstore/ram in the future)
> > to manage storage in zones.
> > +
> > +config PSTORE_BLK
> > + tristate "Log panic/oops to a block device"
> > + depends on PSTORE
> > + depends on BLOCK
> > + select PSTORE_ZONE
> > + default n
> > + help
> > + This enables panic and oops message to be logged to a block dev
> > + where it can be read back at some later point.
> > +
> > + If unsure, say N.
> > +
> > +config PSTORE_BLK_BLKDEV
> > + string "block device identifier"
> > + depends on PSTORE_BLK
> > + default ""
> > + help
> > + Which block device should be used for pstore/blk.
> > +
> > + It accept the following variants:
> > + 1) <hex_major><hex_minor> device number in hexadecimal represents
> > + itself no leading 0x, for example b302.
>
> itself with no leading 0x,
Yes, I've reworked the language here. Thanks!
--
Kees Cook
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
2020-05-11 8:36 ` WeiXiong Liao
@ 2020-05-11 23:08 ` Kees Cook
-1 siblings, 0 replies; 11+ messages in thread
From: Kees Cook @ 2020-05-11 23:08 UTC (permalink / raw)
To: WeiXiong Liao
Cc: Anton Vorontsov, Colin Cross, Tony Luck, Jonathan Corbet,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Petr Mladek, Sergey Senozhatsky, Steven Rostedt, Rob Herring,
Pavel Tatashin, linux-doc, linux-kernel, linux-mtd
On Mon, May 11, 2020 at 04:36:51PM +0800, WeiXiong Liao wrote:
> On 2020/5/11 AM 4:24, Kees Cook wrote:
> > [...]
> > +static struct block_device *psblk_get_bdev(void *holder,
> > + struct bdev_info *info)
>
> Well. That's pretty a good idea to get information about block device
> after registering. And after your codes, the global variable g_bdev_info is
> useless. It's time to drop it.
Ah yes! I meant to clean that up and forgot. Fixed now.
> > [...]
> > + bdev = blkdev_get_by_path(blkdev, mode, holder);
> > + if (IS_ERR(bdev)) {
> > + dev_t devt;
> > +
> > + devt = name_to_dev_t(blkdev);
> > + if (devt == 0)
> > + return ERR_PTR(-ENODEV);
> > + bdev = blkdev_get_by_dev(devt, mode, holder);
> > + }
>
> We should check bdev here. Otherwise, part_nr_sects_read()
> may catch segment error.
>
> if (IS_ERR(bdev))
> return bdev;
Whoops, yes. Fixed.
> > + bdev = psblk_get_bdev(holder, &binfo);
> > + if (IS_ERR(bdev)) {
> > + pr_err("failed to open '%s'!\n", blkdev);
> > + ret = PTR_ERR(bdev);
> > + goto err_put_bdev;
>
> It should not goto err_put_bdev since bdev already be put if get_bdev()
> fail.
Ah yes, good point. Fixed.
--
Kees Cook
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
@ 2020-05-11 23:08 ` Kees Cook
0 siblings, 0 replies; 11+ messages in thread
From: Kees Cook @ 2020-05-11 23:08 UTC (permalink / raw)
To: WeiXiong Liao
Cc: Petr Mladek, Tony Luck, Vignesh Raghavendra, Jonathan Corbet,
Richard Weinberger, Anton Vorontsov, linux-doc, linux-kernel,
Steven Rostedt, Sergey Senozhatsky, Colin Cross, linux-mtd,
Miquel Raynal, Rob Herring, Pavel Tatashin
On Mon, May 11, 2020 at 04:36:51PM +0800, WeiXiong Liao wrote:
> On 2020/5/11 AM 4:24, Kees Cook wrote:
> > [...]
> > +static struct block_device *psblk_get_bdev(void *holder,
> > + struct bdev_info *info)
>
> Well. That's pretty a good idea to get information about block device
> after registering. And after your codes, the global variable g_bdev_info is
> useless. It's time to drop it.
Ah yes! I meant to clean that up and forgot. Fixed now.
> > [...]
> > + bdev = blkdev_get_by_path(blkdev, mode, holder);
> > + if (IS_ERR(bdev)) {
> > + dev_t devt;
> > +
> > + devt = name_to_dev_t(blkdev);
> > + if (devt == 0)
> > + return ERR_PTR(-ENODEV);
> > + bdev = blkdev_get_by_dev(devt, mode, holder);
> > + }
>
> We should check bdev here. Otherwise, part_nr_sects_read()
> may catch segment error.
>
> if (IS_ERR(bdev))
> return bdev;
Whoops, yes. Fixed.
> > + bdev = psblk_get_bdev(holder, &binfo);
> > + if (IS_ERR(bdev)) {
> > + pr_err("failed to open '%s'!\n", blkdev);
> > + ret = PTR_ERR(bdev);
> > + goto err_put_bdev;
>
> It should not goto err_put_bdev since bdev already be put if get_bdev()
> fail.
Ah yes, good point. Fixed.
--
Kees Cook
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
2020-05-10 20:24 ` Kees Cook
@ 2020-05-11 15:36 ` Randy Dunlap
-1 siblings, 0 replies; 11+ messages in thread
From: Randy Dunlap @ 2020-05-11 15:36 UTC (permalink / raw)
To: Kees Cook, WeiXiong Liao
Cc: Anton Vorontsov, Colin Cross, Tony Luck, Jonathan Corbet,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Petr Mladek, Sergey Senozhatsky, Steven Rostedt, Rob Herring,
Pavel Tatashin, linux-doc, linux-kernel, linux-mtd
On 5/10/20 1:24 PM, Kees Cook wrote:
> diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> index 98d2457bdd9f..92ba73bd0b62 100644
> --- a/fs/pstore/Kconfig
> +++ b/fs/pstore/Kconfig
> @@ -160,3 +160,67 @@ config PSTORE_ZONE
> help
> The common layer for pstore/blk (and pstore/ram in the future)
> to manage storage in zones.
> +
> +config PSTORE_BLK
> + tristate "Log panic/oops to a block device"
> + depends on PSTORE
> + depends on BLOCK
> + select PSTORE_ZONE
> + default n
> + help
> + This enables panic and oops message to be logged to a block dev
> + where it can be read back at some later point.
> +
> + If unsure, say N.
> +
> +config PSTORE_BLK_BLKDEV
> + string "block device identifier"
> + depends on PSTORE_BLK
> + default ""
> + help
> + Which block device should be used for pstore/blk.
> +
> + It accept the following variants:
> + 1) <hex_major><hex_minor> device number in hexadecimal represents
> + itself no leading 0x, for example b302.
itself with no leading 0x,
> + 2) /dev/<disk_name> represents the device number of disk
> + 3) /dev/<disk_name><decimal> represents the device number
> + of partition - device number of disk plus the partition number
> + 4) /dev/<disk_name>p<decimal> - same as the above, this form is
> + used when disk name of partitioned disk ends with a digit.
> + 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
> + unique id of a partition if the partition table provides it.
> + The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
> + partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
> + filled hex representation of the 32-bit "NT disk signature", and PP
> + is a zero-filled hex representation of the 1-based partition number.
> + 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
> + to a partition with a known unique id.
> + 7) <major>:<minor> major and minor number of the device separated by
> + a colon.
--
~Randy
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
@ 2020-05-11 15:36 ` Randy Dunlap
0 siblings, 0 replies; 11+ messages in thread
From: Randy Dunlap @ 2020-05-11 15:36 UTC (permalink / raw)
To: Kees Cook, WeiXiong Liao
Cc: Petr Mladek, Tony Luck, Vignesh Raghavendra, Jonathan Corbet,
Richard Weinberger, Anton Vorontsov, linux-doc, linux-kernel,
Steven Rostedt, Sergey Senozhatsky, Colin Cross, linux-mtd,
Miquel Raynal, Rob Herring, Pavel Tatashin
On 5/10/20 1:24 PM, Kees Cook wrote:
> diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> index 98d2457bdd9f..92ba73bd0b62 100644
> --- a/fs/pstore/Kconfig
> +++ b/fs/pstore/Kconfig
> @@ -160,3 +160,67 @@ config PSTORE_ZONE
> help
> The common layer for pstore/blk (and pstore/ram in the future)
> to manage storage in zones.
> +
> +config PSTORE_BLK
> + tristate "Log panic/oops to a block device"
> + depends on PSTORE
> + depends on BLOCK
> + select PSTORE_ZONE
> + default n
> + help
> + This enables panic and oops message to be logged to a block dev
> + where it can be read back at some later point.
> +
> + If unsure, say N.
> +
> +config PSTORE_BLK_BLKDEV
> + string "block device identifier"
> + depends on PSTORE_BLK
> + default ""
> + help
> + Which block device should be used for pstore/blk.
> +
> + It accept the following variants:
> + 1) <hex_major><hex_minor> device number in hexadecimal represents
> + itself no leading 0x, for example b302.
itself with no leading 0x,
> + 2) /dev/<disk_name> represents the device number of disk
> + 3) /dev/<disk_name><decimal> represents the device number
> + of partition - device number of disk plus the partition number
> + 4) /dev/<disk_name>p<decimal> - same as the above, this form is
> + used when disk name of partitioned disk ends with a digit.
> + 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
> + unique id of a partition if the partition table provides it.
> + The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
> + partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
> + filled hex representation of the 32-bit "NT disk signature", and PP
> + is a zero-filled hex representation of the 1-based partition number.
> + 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
> + to a partition with a known unique id.
> + 7) <major>:<minor> major and minor number of the device separated by
> + a colon.
--
~Randy
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
2020-05-10 20:24 ` Kees Cook
@ 2020-05-11 8:36 ` WeiXiong Liao
-1 siblings, 0 replies; 11+ messages in thread
From: WeiXiong Liao @ 2020-05-11 8:36 UTC (permalink / raw)
To: Kees Cook
Cc: Anton Vorontsov, Colin Cross, Tony Luck, Jonathan Corbet,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Petr Mladek, Sergey Senozhatsky, Steven Rostedt, Rob Herring,
Pavel Tatashin, linux-doc, linux-kernel, linux-mtd
hi Kees Cook,
On 2020/5/11 AM 4:24, Kees Cook wrote:
> From: WeiXiong Liao <liaoweixiong@allwinnertech.com>
>
> pstore/blk is similar to pstore/ram, but uses a block device as the
> storage rather than persistent ram.
>
> The pstore/blk backend solves two common use-cases that used to preclude
> using pstore/ram:
> - not all devices have a battery that could be used to persist
> regular RAM across power failures.
> - most embedded intelligent equipment have no persistent ram, which
> increases costs, instead preferring cheaper solutions, like block
> devices.
>
> pstore/blk provides separate configurations for the end user and for the
> block drivers. User configuration determines how pstore/blk operates, such
> as record sizes, max kmsg dump reasons, etc. These can be set by Kconfig
> and/or module parameters, but module parameter have priority over Kconfig.
> Driver configuration covers all the details about the target block device,
> such as total size of the device and how to perform read/write operations.
> These are provided by block drivers, calling pstore_register_blkdev(),
> including an optional panic_write callback used to bypass regular IO
> APIs in an effort to avoid potentially destabilized kernel code during
> a panic.
>
> Signed-off-by: WeiXiong Liao <liaoweixiong@allwinnertech.com>
> Link: https://lore.kernel.org/r/1585126506-18635-3-git-send-email-liaoweixiong@allwinnertech.com
> Co-developed-by: Kees Cook <keescook@chromium.org>
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
> fs/pstore/Kconfig | 64 ++++++
> fs/pstore/Makefile | 3 +
> fs/pstore/blk.c | 436 +++++++++++++++++++++++++++++++++++++
> include/linux/pstore_blk.h | 51 +++++
> 4 files changed, 554 insertions(+)
> create mode 100644 fs/pstore/blk.c
> create mode 100644 include/linux/pstore_blk.h
>
> diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> index 98d2457bdd9f..92ba73bd0b62 100644
> --- a/fs/pstore/Kconfig
> +++ b/fs/pstore/Kconfig
> @@ -160,3 +160,67 @@ config PSTORE_ZONE
> help
> The common layer for pstore/blk (and pstore/ram in the future)
> to manage storage in zones.
> +
> +config PSTORE_BLK
> + tristate "Log panic/oops to a block device"
> + depends on PSTORE
> + depends on BLOCK
> + select PSTORE_ZONE
> + default n
> + help
> + This enables panic and oops message to be logged to a block dev
> + where it can be read back at some later point.
> +
> + If unsure, say N.
> +
> +config PSTORE_BLK_BLKDEV
> + string "block device identifier"
> + depends on PSTORE_BLK
> + default ""
> + help
> + Which block device should be used for pstore/blk.
> +
> + It accept the following variants:
> + 1) <hex_major><hex_minor> device number in hexadecimal represents
> + itself no leading 0x, for example b302.
> + 2) /dev/<disk_name> represents the device number of disk
> + 3) /dev/<disk_name><decimal> represents the device number
> + of partition - device number of disk plus the partition number
> + 4) /dev/<disk_name>p<decimal> - same as the above, this form is
> + used when disk name of partitioned disk ends with a digit.
> + 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
> + unique id of a partition if the partition table provides it.
> + The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
> + partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
> + filled hex representation of the 32-bit "NT disk signature", and PP
> + is a zero-filled hex representation of the 1-based partition number.
> + 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
> + to a partition with a known unique id.
> + 7) <major>:<minor> major and minor number of the device separated by
> + a colon.
> +
> + NOTE that, both Kconfig and module parameters can configure
> + pstore/blk, but module parameters have priority over Kconfig.
> +
> +config PSTORE_BLK_KMSG_SIZE
> + int "Size in Kbytes of kmsg dump log to store"
> + depends on PSTORE_BLK
> + default 64
> + help
> + This just sets size of kmsg dump (oops, panic, etc) log for
> + pstore/blk. The size is in KB and must be a multiple of 4.
> +
> + NOTE that, both Kconfig and module parameters can configure
> + pstore/blk, but module parameters have priority over Kconfig.
> +
> +config PSTORE_BLK_MAX_REASON
> + int "Maximum kmsg dump reason to store"
> + depends on PSTORE_BLK
> + default 2
> + help
> + The maximum reason for kmsg dumps to store. The default is
> + 2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
> + enum kmsg_dump_reason for more details.
> +
> + NOTE that, both Kconfig and module parameters can configure
> + pstore/blk, but module parameters have priority over Kconfig.
> diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
> index 58a967cbe4af..c270467aeece 100644
> --- a/fs/pstore/Makefile
> +++ b/fs/pstore/Makefile
> @@ -15,3 +15,6 @@ obj-$(CONFIG_PSTORE_RAM) += ramoops.o
>
> pstore_zone-objs += zone.o
> obj-$(CONFIG_PSTORE_ZONE) += pstore_zone.o
> +
> +pstore_blk-objs += blk.o
> +obj-$(CONFIG_PSTORE_BLK) += pstore_blk.o
> diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c
> new file mode 100644
> index 000000000000..cec1fa261d1b
> --- /dev/null
> +++ b/fs/pstore/blk.c
> @@ -0,0 +1,436 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Implements pstore backend driver that write to block (or non-block) storage
> + * devices, using the pstore/zone API.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include "../../block/blk.h"
> +#include <linux/blkdev.h>
> +#include <linux/string.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pstore_blk.h>
> +#include <linux/mount.h>
> +#include <linux/uio.h>
> +
> +static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
> +module_param(kmsg_size, long, 0400);
> +MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
> +
> +static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
> +module_param(max_reason, int, 0400);
> +MODULE_PARM_DESC(max_reason,
> + "maximum reason for kmsg dump (default 2: Oops and Panic)");
> +
> +/*
> + * blkdev - the block device to use for pstore storage
> + *
> + * Usually, this will be a partition of a block device.
> + *
> + * blkdev accepts the following variants:
> + * 1) <hex_major><hex_minor> device number in hexadecimal represents itself
> + * no leading 0x, for example b302.
> + * 2) /dev/<disk_name> represents the device number of disk
> + * 3) /dev/<disk_name><decimal> represents the device number
> + * of partition - device number of disk plus the partition number
> + * 4) /dev/<disk_name>p<decimal> - same as the above, that form is
> + * used when disk name of partitioned disk ends on a digit.
> + * 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
> + * unique id of a partition if the partition table provides it.
> + * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
> + * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
> + * filled hex representation of the 32-bit "NT disk signature", and PP
> + * is a zero-filled hex representation of the 1-based partition number.
> + * 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
> + * a partition with a known unique id.
> + * 7) <major>:<minor> major and minor number of the device separated by
> + * a colon.
> + */
> +static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
> +module_param_string(blkdev, blkdev, 80, 0400);
> +MODULE_PARM_DESC(blkdev, "block device for pstore storage");
> +
> +/*
> + * All globals must only be accessed under the pstore_blk_lock
> + * during the register/unregister functions.
> + */
> +static DEFINE_MUTEX(pstore_blk_lock);
> +static struct block_device *psblk_bdev;
> +static struct pstore_zone_info *pstore_zone_info;
> +static pstore_blk_panic_write_op blkdev_panic_write;
> +static struct bdev_info {
> + dev_t devt;
> + sector_t nr_sects;
> + sector_t start_sect;
> +} g_bdev_info;
> +
> +/**
> + * struct pstore_device_info - back-end pstore/blk driver structure.
> + *
> + * @total_size: The total size in bytes pstore/blk can use. It must be greater
> + * than 4096 and be multiple of 4096.
> + * @flags: Refer to macro starting with PSTORE_FLAGS defined in
> + * linux/pstore.h. It means what front-ends this device support.
> + * Zero means all backends for compatible.
> + * @read: The general read operation. Both of the function parameters
> + * @size and @offset are relative value to bock device (not the
> + * whole disk).
> + * On success, the number of bytes should be returned, others
> + * means error.
> + * @write: The same as @read.
> + * @panic_write:The write operation only used for panic case. It's optional
> + * if you do not care panic log. The parameters and return value
> + * are the same as @read.
> + */
> +struct pstore_device_info {
> + unsigned long total_size;
> + unsigned int flags;
> + pstore_zone_read_op read;
> + pstore_zone_write_op write;
> + pstore_zone_write_op panic_write;
> +};
> +
> +static int psblk_register_do(struct pstore_device_info *dev)
> +{
> + int ret;
> +
> + if (!dev || !dev->total_size || !dev->read || !dev->write)
> + return -EINVAL;
> +
> + mutex_lock(&pstore_blk_lock);
> +
> + /* someone already registered before */
> + if (pstore_zone_info) {
> + mutex_unlock(&pstore_blk_lock);
> + return -EBUSY;
> + }
> + pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
> + if (!pstore_zone_info) {
> + mutex_unlock(&pstore_blk_lock);
> + return -ENOMEM;
> + }
> +
> + /* zero means not limit on which backends to attempt to store. */
> + if (!dev->flags)
> + dev->flags = UINT_MAX;
> +
> +#define verify_size(name, alignsize, enabled) { \
> + long _##name_ = (enabled) ? (name) : 0; \
> + _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
> + if (_##name_ & ((alignsize) - 1)) { \
> + pr_info(#name " must align to %d\n", \
> + (alignsize)); \
> + _##name_ = ALIGN(name, (alignsize)); \
> + } \
> + name = _##name_ / 1024; \
> + pstore_zone_info->name = _##name_; \
> + }
> +
> + verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
> +#undef verify_size
> +
> + pstore_zone_info->total_size = dev->total_size;
> + pstore_zone_info->max_reason = max_reason;
> + pstore_zone_info->read = dev->read;
> + pstore_zone_info->write = dev->write;
> + pstore_zone_info->panic_write = dev->panic_write;
> + pstore_zone_info->name = KBUILD_MODNAME;
> + pstore_zone_info->owner = THIS_MODULE;
> +
> + ret = register_pstore_zone(pstore_zone_info);
> + if (ret) {
> + kfree(pstore_zone_info);
> + pstore_zone_info = NULL;
> + }
> + mutex_unlock(&pstore_blk_lock);
> + return ret;
> +}
> +
> +static void psblk_unregister_do(struct pstore_device_info *dev)
> +{
> + mutex_lock(&pstore_blk_lock);
> + if (pstore_zone_info && pstore_zone_info->read == dev->read) {
> + unregister_pstore_zone(pstore_zone_info);
> + kfree(pstore_zone_info);
> + pstore_zone_info = NULL;
> + }
> + mutex_unlock(&pstore_blk_lock);
> +}
> +
> +/**
> + * psblk_get_bdev() - open block device
> + *
> + * @holder: Exclusive holder identifier
> + * @info: Information about bdev to fill in
> + *
> + * Return: pointer to block device on success and others on error.
> + *
> + * On success, the returned block_device has reference count of one.
> + */
> +static struct block_device *psblk_get_bdev(void *holder,
> + struct bdev_info *info)
Well. That's pretty a good idea to get information about block device
after registering. And after your codes, the global variable g_bdev_info is
useless. It's time to drop it.
> +{
> + struct block_device *bdev = ERR_PTR(-ENODEV);
> + fmode_t mode = FMODE_READ | FMODE_WRITE;
> + sector_t nr_sects;
> +
> + if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
> + return ERR_PTR(-EINVAL);
> +
> + if (pstore_zone_info)
> + return ERR_PTR(-EBUSY);
> +
> + if (!blkdev[0])
> + return ERR_PTR(-ENODEV);
> +
> + if (holder)
> + mode |= FMODE_EXCL;
> + bdev = blkdev_get_by_path(blkdev, mode, holder);
> + if (IS_ERR(bdev)) {
> + dev_t devt;
> +
> + devt = name_to_dev_t(blkdev);
> + if (devt == 0)
> + return ERR_PTR(-ENODEV);
> + bdev = blkdev_get_by_dev(devt, mode, holder);
> + }
We should check bdev here. Otherwise, part_nr_sects_read()
may catch segment error.
if (IS_ERR(bdev))
return bdev;
> +
> + nr_sects = part_nr_sects_read(bdev->bd_part);
> + if (!nr_sects) {
> + pr_err("not enough space for '%s'\n", blkdev);
> + blkdev_put(bdev, mode);
> + return ERR_PTR(-ENOSPC);
> + }
> +
> + if (info) {
> + info->devt = bdev->bd_dev;
> + info->nr_sects = nr_sects;
> + info->start_sect = get_start_sect(bdev);
> + }
> +
> + return bdev;
> +}
> +
> +static void psblk_put_bdev(struct block_device *bdev, void *holder)
> +{
> + fmode_t mode = FMODE_READ | FMODE_WRITE;
> +
> + if (!bdev)
> + return;
> +
> + if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
> + return;
> +
> + if (holder)
> + mode |= FMODE_EXCL;
> + blkdev_put(bdev, mode);
> +}
> +
> +static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
> +{
> + struct block_device *bdev = psblk_bdev;
> + struct file file;
> + struct kiocb kiocb;
> + struct iov_iter iter;
> + struct kvec iov = {.iov_base = buf, .iov_len = bytes};
> +
> + if (!bdev)
> + return -ENODEV;
> +
> + memset(&file, 0, sizeof(struct file));
> + file.f_mapping = bdev->bd_inode->i_mapping;
> + file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
> + file.f_inode = bdev->bd_inode;
> + file_ra_state_init(&file.f_ra, file.f_mapping);
> +
> + init_sync_kiocb(&kiocb, &file);
> + kiocb.ki_pos = pos;
> + iov_iter_kvec(&iter, READ, &iov, 1, bytes);
> +
> + return generic_file_read_iter(&kiocb, &iter);
> +}
> +
> +static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
> + loff_t pos)
> +{
> + struct block_device *bdev = psblk_bdev;
> + struct iov_iter iter;
> + struct kiocb kiocb;
> + struct file file;
> + ssize_t ret;
> + struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
> +
> + if (!bdev)
> + return -ENODEV;
> +
> + /* Console/Ftrace backend may handle buffer until flush dirty zones */
> + if (in_interrupt() || irqs_disabled())
> + return -EBUSY;
> +
> + memset(&file, 0, sizeof(struct file));
> + file.f_mapping = bdev->bd_inode->i_mapping;
> + file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
> + file.f_inode = bdev->bd_inode;
> +
> + init_sync_kiocb(&kiocb, &file);
> + kiocb.ki_pos = pos;
> + iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
> +
> + inode_lock(bdev->bd_inode);
> + ret = generic_write_checks(&kiocb, &iter);
> + if (ret > 0)
> + ret = generic_perform_write(&file, &iter, pos);
> + inode_unlock(bdev->bd_inode);
> +
> + if (likely(ret > 0)) {
> + const struct file_operations f_op = {.fsync = blkdev_fsync};
> +
> + file.f_op = &f_op;
> + kiocb.ki_pos += ret;
> + ret = generic_write_sync(&kiocb, ret);
> + }
> + return ret;
> +}
> +
> +static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
> + loff_t off)
> +{
> + int ret;
> +
> + if (!blkdev_panic_write)
> + return -EOPNOTSUPP;
> +
> + /* size and off must align to SECTOR_SIZE for block device */
> + ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
> + size >> SECTOR_SHIFT);
> + return ret ? -EIO : size;
> +}
> +
> +static int __register_pstore_blk(struct pstore_blk_info *info)
> +{
> + char bdev_name[BDEVNAME_SIZE];
> + struct block_device *bdev;
> + struct pstore_device_info dev;
> + struct bdev_info binfo;
> + void *holder = blkdev;
> + int ret = -ENODEV;
> +
> + if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
> + return -EINVAL;
> +
> + /* hold bdev exclusively */
> + memset(&binfo, 0, sizeof(binfo));
> + bdev = psblk_get_bdev(holder, &binfo);
> + if (IS_ERR(bdev)) {
> + pr_err("failed to open '%s'!\n", blkdev);
> + ret = PTR_ERR(bdev);
> + goto err_put_bdev;
It should not goto err_put_bdev since bdev already be put if get_bdev()
fail.
> + }
> +
> + /* only allow driver matching the @blkdev */
> + if (!binfo.devt || MAJOR(binfo.devt) != info->major) {
> + pr_debug("invalid major %u (expect %u)\n",
> + info->major, MAJOR(binfo.devt));
> + ret = -ENODEV;
> + goto err_put_bdev;
> + }
> +
> + /* psblk_bdev must be assigned before register to pstore/blk */
> + psblk_bdev = bdev;
> + blkdev_panic_write = info->panic_write;
> +
> + /* Copy back block device details. */
> + info->devt = binfo.devt;
> + info->nr_sects = binfo.nr_sects;
> + info->start_sect = binfo.start_sect;
> +
> + memset(&dev, 0, sizeof(dev));
> + dev.total_size = info->nr_sects << SECTOR_SHIFT;
> + dev.flags = info->flags;
> + dev.read = psblk_generic_blk_read;
> + dev.write = psblk_generic_blk_write;
> + dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
> +
> + ret = psblk_register_do(&dev);
> + if (ret)
> + goto err_put_bdev;
> +
> + bdevname(bdev, bdev_name);
> + pr_info("attached %s%s\n", bdev_name,
> + info->panic_write ? "" : " (no dedicated panic_write!)");
> + return 0;
> +
> +err_put_bdev:
> + psblk_bdev = NULL;
> + blkdev_panic_write = NULL;
> + psblk_put_bdev(bdev, holder);
> + return ret;
> +}
> +
> +/**
> + * register_pstore_blk() - register block device to pstore/blk
> + *
> + * @info: details on the desired block device interface
> + *
> + * Return:
> + * * 0 - OK
> + * * Others - something error.
> + */
> +int register_pstore_blk(struct pstore_blk_info *info)
> +{
> + int ret;
> +
> + mutex_lock(&pstore_blk_lock);
> + ret = __register_pstore_blk(info);
> + mutex_unlock(&pstore_blk_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(register_pstore_blk);
> +
> +static void __unregister_pstore_blk(unsigned int major)
> +{
> + struct pstore_device_info dev = { .read = psblk_generic_blk_read };
> + void *holder = blkdev;
> +
> + WARN_ON(!mutex_is_locked(&pstore_blk_lock));
> + if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
> + psblk_unregister_do(&dev);
> + psblk_put_bdev(psblk_bdev, holder);
> + blkdev_panic_write = NULL;
> + psblk_bdev = NULL;
> + memset(&g_bdev_info, 0, sizeof(g_bdev_info));
Also here.
> + }
> +}
> +
> +/**
> + * unregister_pstore_blk() - unregister block device from pstore/blk
> + *
> + * @major: the major device number of device
> + */
> +void unregister_pstore_blk(unsigned int major)
> +{
> + mutex_lock(&pstore_blk_lock);
> + __unregister_pstore_blk(major);
> + mutex_unlock(&pstore_blk_lock);
> +}
> +EXPORT_SYMBOL_GPL(unregister_pstore_blk);
> +
> +static void __exit pstore_blk_exit(void)
> +{
> + mutex_lock(&pstore_blk_lock);
> + if (psblk_bdev)
> + __unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
> + mutex_unlock(&pstore_blk_lock);
> +}
> +module_exit(pstore_blk_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
> +MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
> +MODULE_DESCRIPTION("pstore backend for block devices");
> diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
> new file mode 100644
> index 000000000000..4501977b1336
> --- /dev/null
> +++ b/include/linux/pstore_blk.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __PSTORE_BLK_H_
> +#define __PSTORE_BLK_H_
> +
> +#include <linux/types.h>
> +#include <linux/pstore.h>
> +#include <linux/pstore_zone.h>
> +
> +/**
> + * typedef pstore_blk_panic_write_op - panic write operation to block device
> + *
> + * @buf: the data to write
> + * @start_sect: start sector to block device
> + * @sects: sectors count on buf
> + *
> + * Return: On success, zero should be returned. Others mean error.
> + *
> + * Panic write to block device must be aligned to SECTOR_SIZE.
> + */
> +typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
> + sector_t sects);
> +
> +/**
> + * struct pstore_blk_info - pstore/blk registration details
> + *
> + * @major: Which major device number to support with pstore/blk
> + * @flags: The supported PSTORE_FLAGS_* from linux/pstore.h.
> + * @panic_write:The write operation only used for the panic case.
> + * This can be NULL, but is recommended to avoid losing
> + * crash data if the kernel's IO path or work queues are
> + * broken during a panic.
> + * @devt: The dev_t that pstore/blk has attached to.
> + * @nr_sects: Number of sectors on @devt.
> + * @start_sect: Starting sector on @devt.
> + */
> +struct pstore_blk_info {
> + unsigned int major;
> + unsigned int flags;
> + pstore_blk_panic_write_op panic_write;
> +
> + /* Filled in by pstore/blk after registration. */
> + dev_t devt;
> + sector_t nr_sects;
> + sector_t start_sect;
> +};
> +
> +int register_pstore_blk(struct pstore_blk_info *info);
> +void unregister_pstore_blk(unsigned int major);
> +
> +#endif
>
--
WeiXiong Liao
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
@ 2020-05-11 8:36 ` WeiXiong Liao
0 siblings, 0 replies; 11+ messages in thread
From: WeiXiong Liao @ 2020-05-11 8:36 UTC (permalink / raw)
To: Kees Cook
Cc: Petr Mladek, Tony Luck, Vignesh Raghavendra, Jonathan Corbet,
Richard Weinberger, Anton Vorontsov, linux-doc, linux-kernel,
Steven Rostedt, Sergey Senozhatsky, Colin Cross, linux-mtd,
Miquel Raynal, Rob Herring, Pavel Tatashin
hi Kees Cook,
On 2020/5/11 AM 4:24, Kees Cook wrote:
> From: WeiXiong Liao <liaoweixiong@allwinnertech.com>
>
> pstore/blk is similar to pstore/ram, but uses a block device as the
> storage rather than persistent ram.
>
> The pstore/blk backend solves two common use-cases that used to preclude
> using pstore/ram:
> - not all devices have a battery that could be used to persist
> regular RAM across power failures.
> - most embedded intelligent equipment have no persistent ram, which
> increases costs, instead preferring cheaper solutions, like block
> devices.
>
> pstore/blk provides separate configurations for the end user and for the
> block drivers. User configuration determines how pstore/blk operates, such
> as record sizes, max kmsg dump reasons, etc. These can be set by Kconfig
> and/or module parameters, but module parameter have priority over Kconfig.
> Driver configuration covers all the details about the target block device,
> such as total size of the device and how to perform read/write operations.
> These are provided by block drivers, calling pstore_register_blkdev(),
> including an optional panic_write callback used to bypass regular IO
> APIs in an effort to avoid potentially destabilized kernel code during
> a panic.
>
> Signed-off-by: WeiXiong Liao <liaoweixiong@allwinnertech.com>
> Link: https://lore.kernel.org/r/1585126506-18635-3-git-send-email-liaoweixiong@allwinnertech.com
> Co-developed-by: Kees Cook <keescook@chromium.org>
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
> fs/pstore/Kconfig | 64 ++++++
> fs/pstore/Makefile | 3 +
> fs/pstore/blk.c | 436 +++++++++++++++++++++++++++++++++++++
> include/linux/pstore_blk.h | 51 +++++
> 4 files changed, 554 insertions(+)
> create mode 100644 fs/pstore/blk.c
> create mode 100644 include/linux/pstore_blk.h
>
> diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
> index 98d2457bdd9f..92ba73bd0b62 100644
> --- a/fs/pstore/Kconfig
> +++ b/fs/pstore/Kconfig
> @@ -160,3 +160,67 @@ config PSTORE_ZONE
> help
> The common layer for pstore/blk (and pstore/ram in the future)
> to manage storage in zones.
> +
> +config PSTORE_BLK
> + tristate "Log panic/oops to a block device"
> + depends on PSTORE
> + depends on BLOCK
> + select PSTORE_ZONE
> + default n
> + help
> + This enables panic and oops message to be logged to a block dev
> + where it can be read back at some later point.
> +
> + If unsure, say N.
> +
> +config PSTORE_BLK_BLKDEV
> + string "block device identifier"
> + depends on PSTORE_BLK
> + default ""
> + help
> + Which block device should be used for pstore/blk.
> +
> + It accept the following variants:
> + 1) <hex_major><hex_minor> device number in hexadecimal represents
> + itself no leading 0x, for example b302.
> + 2) /dev/<disk_name> represents the device number of disk
> + 3) /dev/<disk_name><decimal> represents the device number
> + of partition - device number of disk plus the partition number
> + 4) /dev/<disk_name>p<decimal> - same as the above, this form is
> + used when disk name of partitioned disk ends with a digit.
> + 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
> + unique id of a partition if the partition table provides it.
> + The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
> + partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
> + filled hex representation of the 32-bit "NT disk signature", and PP
> + is a zero-filled hex representation of the 1-based partition number.
> + 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
> + to a partition with a known unique id.
> + 7) <major>:<minor> major and minor number of the device separated by
> + a colon.
> +
> + NOTE that, both Kconfig and module parameters can configure
> + pstore/blk, but module parameters have priority over Kconfig.
> +
> +config PSTORE_BLK_KMSG_SIZE
> + int "Size in Kbytes of kmsg dump log to store"
> + depends on PSTORE_BLK
> + default 64
> + help
> + This just sets size of kmsg dump (oops, panic, etc) log for
> + pstore/blk. The size is in KB and must be a multiple of 4.
> +
> + NOTE that, both Kconfig and module parameters can configure
> + pstore/blk, but module parameters have priority over Kconfig.
> +
> +config PSTORE_BLK_MAX_REASON
> + int "Maximum kmsg dump reason to store"
> + depends on PSTORE_BLK
> + default 2
> + help
> + The maximum reason for kmsg dumps to store. The default is
> + 2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
> + enum kmsg_dump_reason for more details.
> +
> + NOTE that, both Kconfig and module parameters can configure
> + pstore/blk, but module parameters have priority over Kconfig.
> diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
> index 58a967cbe4af..c270467aeece 100644
> --- a/fs/pstore/Makefile
> +++ b/fs/pstore/Makefile
> @@ -15,3 +15,6 @@ obj-$(CONFIG_PSTORE_RAM) += ramoops.o
>
> pstore_zone-objs += zone.o
> obj-$(CONFIG_PSTORE_ZONE) += pstore_zone.o
> +
> +pstore_blk-objs += blk.o
> +obj-$(CONFIG_PSTORE_BLK) += pstore_blk.o
> diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c
> new file mode 100644
> index 000000000000..cec1fa261d1b
> --- /dev/null
> +++ b/fs/pstore/blk.c
> @@ -0,0 +1,436 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Implements pstore backend driver that write to block (or non-block) storage
> + * devices, using the pstore/zone API.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include "../../block/blk.h"
> +#include <linux/blkdev.h>
> +#include <linux/string.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pstore_blk.h>
> +#include <linux/mount.h>
> +#include <linux/uio.h>
> +
> +static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
> +module_param(kmsg_size, long, 0400);
> +MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
> +
> +static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
> +module_param(max_reason, int, 0400);
> +MODULE_PARM_DESC(max_reason,
> + "maximum reason for kmsg dump (default 2: Oops and Panic)");
> +
> +/*
> + * blkdev - the block device to use for pstore storage
> + *
> + * Usually, this will be a partition of a block device.
> + *
> + * blkdev accepts the following variants:
> + * 1) <hex_major><hex_minor> device number in hexadecimal represents itself
> + * no leading 0x, for example b302.
> + * 2) /dev/<disk_name> represents the device number of disk
> + * 3) /dev/<disk_name><decimal> represents the device number
> + * of partition - device number of disk plus the partition number
> + * 4) /dev/<disk_name>p<decimal> - same as the above, that form is
> + * used when disk name of partitioned disk ends on a digit.
> + * 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
> + * unique id of a partition if the partition table provides it.
> + * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
> + * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
> + * filled hex representation of the 32-bit "NT disk signature", and PP
> + * is a zero-filled hex representation of the 1-based partition number.
> + * 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
> + * a partition with a known unique id.
> + * 7) <major>:<minor> major and minor number of the device separated by
> + * a colon.
> + */
> +static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
> +module_param_string(blkdev, blkdev, 80, 0400);
> +MODULE_PARM_DESC(blkdev, "block device for pstore storage");
> +
> +/*
> + * All globals must only be accessed under the pstore_blk_lock
> + * during the register/unregister functions.
> + */
> +static DEFINE_MUTEX(pstore_blk_lock);
> +static struct block_device *psblk_bdev;
> +static struct pstore_zone_info *pstore_zone_info;
> +static pstore_blk_panic_write_op blkdev_panic_write;
> +static struct bdev_info {
> + dev_t devt;
> + sector_t nr_sects;
> + sector_t start_sect;
> +} g_bdev_info;
> +
> +/**
> + * struct pstore_device_info - back-end pstore/blk driver structure.
> + *
> + * @total_size: The total size in bytes pstore/blk can use. It must be greater
> + * than 4096 and be multiple of 4096.
> + * @flags: Refer to macro starting with PSTORE_FLAGS defined in
> + * linux/pstore.h. It means what front-ends this device support.
> + * Zero means all backends for compatible.
> + * @read: The general read operation. Both of the function parameters
> + * @size and @offset are relative value to bock device (not the
> + * whole disk).
> + * On success, the number of bytes should be returned, others
> + * means error.
> + * @write: The same as @read.
> + * @panic_write:The write operation only used for panic case. It's optional
> + * if you do not care panic log. The parameters and return value
> + * are the same as @read.
> + */
> +struct pstore_device_info {
> + unsigned long total_size;
> + unsigned int flags;
> + pstore_zone_read_op read;
> + pstore_zone_write_op write;
> + pstore_zone_write_op panic_write;
> +};
> +
> +static int psblk_register_do(struct pstore_device_info *dev)
> +{
> + int ret;
> +
> + if (!dev || !dev->total_size || !dev->read || !dev->write)
> + return -EINVAL;
> +
> + mutex_lock(&pstore_blk_lock);
> +
> + /* someone already registered before */
> + if (pstore_zone_info) {
> + mutex_unlock(&pstore_blk_lock);
> + return -EBUSY;
> + }
> + pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
> + if (!pstore_zone_info) {
> + mutex_unlock(&pstore_blk_lock);
> + return -ENOMEM;
> + }
> +
> + /* zero means not limit on which backends to attempt to store. */
> + if (!dev->flags)
> + dev->flags = UINT_MAX;
> +
> +#define verify_size(name, alignsize, enabled) { \
> + long _##name_ = (enabled) ? (name) : 0; \
> + _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
> + if (_##name_ & ((alignsize) - 1)) { \
> + pr_info(#name " must align to %d\n", \
> + (alignsize)); \
> + _##name_ = ALIGN(name, (alignsize)); \
> + } \
> + name = _##name_ / 1024; \
> + pstore_zone_info->name = _##name_; \
> + }
> +
> + verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
> +#undef verify_size
> +
> + pstore_zone_info->total_size = dev->total_size;
> + pstore_zone_info->max_reason = max_reason;
> + pstore_zone_info->read = dev->read;
> + pstore_zone_info->write = dev->write;
> + pstore_zone_info->panic_write = dev->panic_write;
> + pstore_zone_info->name = KBUILD_MODNAME;
> + pstore_zone_info->owner = THIS_MODULE;
> +
> + ret = register_pstore_zone(pstore_zone_info);
> + if (ret) {
> + kfree(pstore_zone_info);
> + pstore_zone_info = NULL;
> + }
> + mutex_unlock(&pstore_blk_lock);
> + return ret;
> +}
> +
> +static void psblk_unregister_do(struct pstore_device_info *dev)
> +{
> + mutex_lock(&pstore_blk_lock);
> + if (pstore_zone_info && pstore_zone_info->read == dev->read) {
> + unregister_pstore_zone(pstore_zone_info);
> + kfree(pstore_zone_info);
> + pstore_zone_info = NULL;
> + }
> + mutex_unlock(&pstore_blk_lock);
> +}
> +
> +/**
> + * psblk_get_bdev() - open block device
> + *
> + * @holder: Exclusive holder identifier
> + * @info: Information about bdev to fill in
> + *
> + * Return: pointer to block device on success and others on error.
> + *
> + * On success, the returned block_device has reference count of one.
> + */
> +static struct block_device *psblk_get_bdev(void *holder,
> + struct bdev_info *info)
Well. That's pretty a good idea to get information about block device
after registering. And after your codes, the global variable g_bdev_info is
useless. It's time to drop it.
> +{
> + struct block_device *bdev = ERR_PTR(-ENODEV);
> + fmode_t mode = FMODE_READ | FMODE_WRITE;
> + sector_t nr_sects;
> +
> + if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
> + return ERR_PTR(-EINVAL);
> +
> + if (pstore_zone_info)
> + return ERR_PTR(-EBUSY);
> +
> + if (!blkdev[0])
> + return ERR_PTR(-ENODEV);
> +
> + if (holder)
> + mode |= FMODE_EXCL;
> + bdev = blkdev_get_by_path(blkdev, mode, holder);
> + if (IS_ERR(bdev)) {
> + dev_t devt;
> +
> + devt = name_to_dev_t(blkdev);
> + if (devt == 0)
> + return ERR_PTR(-ENODEV);
> + bdev = blkdev_get_by_dev(devt, mode, holder);
> + }
We should check bdev here. Otherwise, part_nr_sects_read()
may catch segment error.
if (IS_ERR(bdev))
return bdev;
> +
> + nr_sects = part_nr_sects_read(bdev->bd_part);
> + if (!nr_sects) {
> + pr_err("not enough space for '%s'\n", blkdev);
> + blkdev_put(bdev, mode);
> + return ERR_PTR(-ENOSPC);
> + }
> +
> + if (info) {
> + info->devt = bdev->bd_dev;
> + info->nr_sects = nr_sects;
> + info->start_sect = get_start_sect(bdev);
> + }
> +
> + return bdev;
> +}
> +
> +static void psblk_put_bdev(struct block_device *bdev, void *holder)
> +{
> + fmode_t mode = FMODE_READ | FMODE_WRITE;
> +
> + if (!bdev)
> + return;
> +
> + if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
> + return;
> +
> + if (holder)
> + mode |= FMODE_EXCL;
> + blkdev_put(bdev, mode);
> +}
> +
> +static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
> +{
> + struct block_device *bdev = psblk_bdev;
> + struct file file;
> + struct kiocb kiocb;
> + struct iov_iter iter;
> + struct kvec iov = {.iov_base = buf, .iov_len = bytes};
> +
> + if (!bdev)
> + return -ENODEV;
> +
> + memset(&file, 0, sizeof(struct file));
> + file.f_mapping = bdev->bd_inode->i_mapping;
> + file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
> + file.f_inode = bdev->bd_inode;
> + file_ra_state_init(&file.f_ra, file.f_mapping);
> +
> + init_sync_kiocb(&kiocb, &file);
> + kiocb.ki_pos = pos;
> + iov_iter_kvec(&iter, READ, &iov, 1, bytes);
> +
> + return generic_file_read_iter(&kiocb, &iter);
> +}
> +
> +static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
> + loff_t pos)
> +{
> + struct block_device *bdev = psblk_bdev;
> + struct iov_iter iter;
> + struct kiocb kiocb;
> + struct file file;
> + ssize_t ret;
> + struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
> +
> + if (!bdev)
> + return -ENODEV;
> +
> + /* Console/Ftrace backend may handle buffer until flush dirty zones */
> + if (in_interrupt() || irqs_disabled())
> + return -EBUSY;
> +
> + memset(&file, 0, sizeof(struct file));
> + file.f_mapping = bdev->bd_inode->i_mapping;
> + file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
> + file.f_inode = bdev->bd_inode;
> +
> + init_sync_kiocb(&kiocb, &file);
> + kiocb.ki_pos = pos;
> + iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
> +
> + inode_lock(bdev->bd_inode);
> + ret = generic_write_checks(&kiocb, &iter);
> + if (ret > 0)
> + ret = generic_perform_write(&file, &iter, pos);
> + inode_unlock(bdev->bd_inode);
> +
> + if (likely(ret > 0)) {
> + const struct file_operations f_op = {.fsync = blkdev_fsync};
> +
> + file.f_op = &f_op;
> + kiocb.ki_pos += ret;
> + ret = generic_write_sync(&kiocb, ret);
> + }
> + return ret;
> +}
> +
> +static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
> + loff_t off)
> +{
> + int ret;
> +
> + if (!blkdev_panic_write)
> + return -EOPNOTSUPP;
> +
> + /* size and off must align to SECTOR_SIZE for block device */
> + ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
> + size >> SECTOR_SHIFT);
> + return ret ? -EIO : size;
> +}
> +
> +static int __register_pstore_blk(struct pstore_blk_info *info)
> +{
> + char bdev_name[BDEVNAME_SIZE];
> + struct block_device *bdev;
> + struct pstore_device_info dev;
> + struct bdev_info binfo;
> + void *holder = blkdev;
> + int ret = -ENODEV;
> +
> + if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
> + return -EINVAL;
> +
> + /* hold bdev exclusively */
> + memset(&binfo, 0, sizeof(binfo));
> + bdev = psblk_get_bdev(holder, &binfo);
> + if (IS_ERR(bdev)) {
> + pr_err("failed to open '%s'!\n", blkdev);
> + ret = PTR_ERR(bdev);
> + goto err_put_bdev;
It should not goto err_put_bdev since bdev already be put if get_bdev()
fail.
> + }
> +
> + /* only allow driver matching the @blkdev */
> + if (!binfo.devt || MAJOR(binfo.devt) != info->major) {
> + pr_debug("invalid major %u (expect %u)\n",
> + info->major, MAJOR(binfo.devt));
> + ret = -ENODEV;
> + goto err_put_bdev;
> + }
> +
> + /* psblk_bdev must be assigned before register to pstore/blk */
> + psblk_bdev = bdev;
> + blkdev_panic_write = info->panic_write;
> +
> + /* Copy back block device details. */
> + info->devt = binfo.devt;
> + info->nr_sects = binfo.nr_sects;
> + info->start_sect = binfo.start_sect;
> +
> + memset(&dev, 0, sizeof(dev));
> + dev.total_size = info->nr_sects << SECTOR_SHIFT;
> + dev.flags = info->flags;
> + dev.read = psblk_generic_blk_read;
> + dev.write = psblk_generic_blk_write;
> + dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
> +
> + ret = psblk_register_do(&dev);
> + if (ret)
> + goto err_put_bdev;
> +
> + bdevname(bdev, bdev_name);
> + pr_info("attached %s%s\n", bdev_name,
> + info->panic_write ? "" : " (no dedicated panic_write!)");
> + return 0;
> +
> +err_put_bdev:
> + psblk_bdev = NULL;
> + blkdev_panic_write = NULL;
> + psblk_put_bdev(bdev, holder);
> + return ret;
> +}
> +
> +/**
> + * register_pstore_blk() - register block device to pstore/blk
> + *
> + * @info: details on the desired block device interface
> + *
> + * Return:
> + * * 0 - OK
> + * * Others - something error.
> + */
> +int register_pstore_blk(struct pstore_blk_info *info)
> +{
> + int ret;
> +
> + mutex_lock(&pstore_blk_lock);
> + ret = __register_pstore_blk(info);
> + mutex_unlock(&pstore_blk_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(register_pstore_blk);
> +
> +static void __unregister_pstore_blk(unsigned int major)
> +{
> + struct pstore_device_info dev = { .read = psblk_generic_blk_read };
> + void *holder = blkdev;
> +
> + WARN_ON(!mutex_is_locked(&pstore_blk_lock));
> + if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
> + psblk_unregister_do(&dev);
> + psblk_put_bdev(psblk_bdev, holder);
> + blkdev_panic_write = NULL;
> + psblk_bdev = NULL;
> + memset(&g_bdev_info, 0, sizeof(g_bdev_info));
Also here.
> + }
> +}
> +
> +/**
> + * unregister_pstore_blk() - unregister block device from pstore/blk
> + *
> + * @major: the major device number of device
> + */
> +void unregister_pstore_blk(unsigned int major)
> +{
> + mutex_lock(&pstore_blk_lock);
> + __unregister_pstore_blk(major);
> + mutex_unlock(&pstore_blk_lock);
> +}
> +EXPORT_SYMBOL_GPL(unregister_pstore_blk);
> +
> +static void __exit pstore_blk_exit(void)
> +{
> + mutex_lock(&pstore_blk_lock);
> + if (psblk_bdev)
> + __unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
> + mutex_unlock(&pstore_blk_lock);
> +}
> +module_exit(pstore_blk_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
> +MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
> +MODULE_DESCRIPTION("pstore backend for block devices");
> diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
> new file mode 100644
> index 000000000000..4501977b1336
> --- /dev/null
> +++ b/include/linux/pstore_blk.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __PSTORE_BLK_H_
> +#define __PSTORE_BLK_H_
> +
> +#include <linux/types.h>
> +#include <linux/pstore.h>
> +#include <linux/pstore_zone.h>
> +
> +/**
> + * typedef pstore_blk_panic_write_op - panic write operation to block device
> + *
> + * @buf: the data to write
> + * @start_sect: start sector to block device
> + * @sects: sectors count on buf
> + *
> + * Return: On success, zero should be returned. Others mean error.
> + *
> + * Panic write to block device must be aligned to SECTOR_SIZE.
> + */
> +typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
> + sector_t sects);
> +
> +/**
> + * struct pstore_blk_info - pstore/blk registration details
> + *
> + * @major: Which major device number to support with pstore/blk
> + * @flags: The supported PSTORE_FLAGS_* from linux/pstore.h.
> + * @panic_write:The write operation only used for the panic case.
> + * This can be NULL, but is recommended to avoid losing
> + * crash data if the kernel's IO path or work queues are
> + * broken during a panic.
> + * @devt: The dev_t that pstore/blk has attached to.
> + * @nr_sects: Number of sectors on @devt.
> + * @start_sect: Starting sector on @devt.
> + */
> +struct pstore_blk_info {
> + unsigned int major;
> + unsigned int flags;
> + pstore_blk_panic_write_op panic_write;
> +
> + /* Filled in by pstore/blk after registration. */
> + dev_t devt;
> + sector_t nr_sects;
> + sector_t start_sect;
> +};
> +
> +int register_pstore_blk(struct pstore_blk_info *info);
> +void unregister_pstore_blk(unsigned int major);
> +
> +#endif
>
--
WeiXiong Liao
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
2020-05-10 20:24 [PATCH v7 00/18] pstore: mtd: support crash log to block and mtd device Kees Cook
@ 2020-05-10 20:24 ` Kees Cook
0 siblings, 0 replies; 11+ messages in thread
From: Kees Cook @ 2020-05-10 20:24 UTC (permalink / raw)
To: WeiXiong Liao
Cc: Kees Cook, Anton Vorontsov, Colin Cross, Tony Luck,
Jonathan Corbet, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Petr Mladek, Sergey Senozhatsky,
Steven Rostedt, Rob Herring, Pavel Tatashin, linux-doc,
linux-kernel, linux-mtd
From: WeiXiong Liao <liaoweixiong@allwinnertech.com>
pstore/blk is similar to pstore/ram, but uses a block device as the
storage rather than persistent ram.
The pstore/blk backend solves two common use-cases that used to preclude
using pstore/ram:
- not all devices have a battery that could be used to persist
regular RAM across power failures.
- most embedded intelligent equipment have no persistent ram, which
increases costs, instead preferring cheaper solutions, like block
devices.
pstore/blk provides separate configurations for the end user and for the
block drivers. User configuration determines how pstore/blk operates, such
as record sizes, max kmsg dump reasons, etc. These can be set by Kconfig
and/or module parameters, but module parameter have priority over Kconfig.
Driver configuration covers all the details about the target block device,
such as total size of the device and how to perform read/write operations.
These are provided by block drivers, calling pstore_register_blkdev(),
including an optional panic_write callback used to bypass regular IO
APIs in an effort to avoid potentially destabilized kernel code during
a panic.
Signed-off-by: WeiXiong Liao <liaoweixiong@allwinnertech.com>
Link: https://lore.kernel.org/r/1585126506-18635-3-git-send-email-liaoweixiong@allwinnertech.com
Co-developed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
fs/pstore/Kconfig | 64 ++++++
fs/pstore/Makefile | 3 +
fs/pstore/blk.c | 436 +++++++++++++++++++++++++++++++++++++
include/linux/pstore_blk.h | 51 +++++
4 files changed, 554 insertions(+)
create mode 100644 fs/pstore/blk.c
create mode 100644 include/linux/pstore_blk.h
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index 98d2457bdd9f..92ba73bd0b62 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -160,3 +160,67 @@ config PSTORE_ZONE
help
The common layer for pstore/blk (and pstore/ram in the future)
to manage storage in zones.
+
+config PSTORE_BLK
+ tristate "Log panic/oops to a block device"
+ depends on PSTORE
+ depends on BLOCK
+ select PSTORE_ZONE
+ default n
+ help
+ This enables panic and oops message to be logged to a block dev
+ where it can be read back at some later point.
+
+ If unsure, say N.
+
+config PSTORE_BLK_BLKDEV
+ string "block device identifier"
+ depends on PSTORE_BLK
+ default ""
+ help
+ Which block device should be used for pstore/blk.
+
+ It accept the following variants:
+ 1) <hex_major><hex_minor> device number in hexadecimal represents
+ itself no leading 0x, for example b302.
+ 2) /dev/<disk_name> represents the device number of disk
+ 3) /dev/<disk_name><decimal> represents the device number
+ of partition - device number of disk plus the partition number
+ 4) /dev/<disk_name>p<decimal> - same as the above, this form is
+ used when disk name of partitioned disk ends with a digit.
+ 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
+ unique id of a partition if the partition table provides it.
+ The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
+ partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
+ filled hex representation of the 32-bit "NT disk signature", and PP
+ is a zero-filled hex representation of the 1-based partition number.
+ 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
+ to a partition with a known unique id.
+ 7) <major>:<minor> major and minor number of the device separated by
+ a colon.
+
+ NOTE that, both Kconfig and module parameters can configure
+ pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_KMSG_SIZE
+ int "Size in Kbytes of kmsg dump log to store"
+ depends on PSTORE_BLK
+ default 64
+ help
+ This just sets size of kmsg dump (oops, panic, etc) log for
+ pstore/blk. The size is in KB and must be a multiple of 4.
+
+ NOTE that, both Kconfig and module parameters can configure
+ pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_MAX_REASON
+ int "Maximum kmsg dump reason to store"
+ depends on PSTORE_BLK
+ default 2
+ help
+ The maximum reason for kmsg dumps to store. The default is
+ 2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
+ enum kmsg_dump_reason for more details.
+
+ NOTE that, both Kconfig and module parameters can configure
+ pstore/blk, but module parameters have priority over Kconfig.
diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
index 58a967cbe4af..c270467aeece 100644
--- a/fs/pstore/Makefile
+++ b/fs/pstore/Makefile
@@ -15,3 +15,6 @@ obj-$(CONFIG_PSTORE_RAM) += ramoops.o
pstore_zone-objs += zone.o
obj-$(CONFIG_PSTORE_ZONE) += pstore_zone.o
+
+pstore_blk-objs += blk.o
+obj-$(CONFIG_PSTORE_BLK) += pstore_blk.o
diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c
new file mode 100644
index 000000000000..cec1fa261d1b
--- /dev/null
+++ b/fs/pstore/blk.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements pstore backend driver that write to block (or non-block) storage
+ * devices, using the pstore/zone API.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "../../block/blk.h"
+#include <linux/blkdev.h>
+#include <linux/string.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pstore_blk.h>
+#include <linux/mount.h>
+#include <linux/uio.h>
+
+static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
+module_param(kmsg_size, long, 0400);
+MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
+
+static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
+module_param(max_reason, int, 0400);
+MODULE_PARM_DESC(max_reason,
+ "maximum reason for kmsg dump (default 2: Oops and Panic)");
+
+/*
+ * blkdev - the block device to use for pstore storage
+ *
+ * Usually, this will be a partition of a block device.
+ *
+ * blkdev accepts the following variants:
+ * 1) <hex_major><hex_minor> device number in hexadecimal represents itself
+ * no leading 0x, for example b302.
+ * 2) /dev/<disk_name> represents the device number of disk
+ * 3) /dev/<disk_name><decimal> represents the device number
+ * of partition - device number of disk plus the partition number
+ * 4) /dev/<disk_name>p<decimal> - same as the above, that form is
+ * used when disk name of partitioned disk ends on a digit.
+ * 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
+ * unique id of a partition if the partition table provides it.
+ * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
+ * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
+ * filled hex representation of the 32-bit "NT disk signature", and PP
+ * is a zero-filled hex representation of the 1-based partition number.
+ * 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
+ * a partition with a known unique id.
+ * 7) <major>:<minor> major and minor number of the device separated by
+ * a colon.
+ */
+static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
+module_param_string(blkdev, blkdev, 80, 0400);
+MODULE_PARM_DESC(blkdev, "block device for pstore storage");
+
+/*
+ * All globals must only be accessed under the pstore_blk_lock
+ * during the register/unregister functions.
+ */
+static DEFINE_MUTEX(pstore_blk_lock);
+static struct block_device *psblk_bdev;
+static struct pstore_zone_info *pstore_zone_info;
+static pstore_blk_panic_write_op blkdev_panic_write;
+static struct bdev_info {
+ dev_t devt;
+ sector_t nr_sects;
+ sector_t start_sect;
+} g_bdev_info;
+
+/**
+ * struct pstore_device_info - back-end pstore/blk driver structure.
+ *
+ * @total_size: The total size in bytes pstore/blk can use. It must be greater
+ * than 4096 and be multiple of 4096.
+ * @flags: Refer to macro starting with PSTORE_FLAGS defined in
+ * linux/pstore.h. It means what front-ends this device support.
+ * Zero means all backends for compatible.
+ * @read: The general read operation. Both of the function parameters
+ * @size and @offset are relative value to bock device (not the
+ * whole disk).
+ * On success, the number of bytes should be returned, others
+ * means error.
+ * @write: The same as @read.
+ * @panic_write:The write operation only used for panic case. It's optional
+ * if you do not care panic log. The parameters and return value
+ * are the same as @read.
+ */
+struct pstore_device_info {
+ unsigned long total_size;
+ unsigned int flags;
+ pstore_zone_read_op read;
+ pstore_zone_write_op write;
+ pstore_zone_write_op panic_write;
+};
+
+static int psblk_register_do(struct pstore_device_info *dev)
+{
+ int ret;
+
+ if (!dev || !dev->total_size || !dev->read || !dev->write)
+ return -EINVAL;
+
+ mutex_lock(&pstore_blk_lock);
+
+ /* someone already registered before */
+ if (pstore_zone_info) {
+ mutex_unlock(&pstore_blk_lock);
+ return -EBUSY;
+ }
+ pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
+ if (!pstore_zone_info) {
+ mutex_unlock(&pstore_blk_lock);
+ return -ENOMEM;
+ }
+
+ /* zero means not limit on which backends to attempt to store. */
+ if (!dev->flags)
+ dev->flags = UINT_MAX;
+
+#define verify_size(name, alignsize, enabled) { \
+ long _##name_ = (enabled) ? (name) : 0; \
+ _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
+ if (_##name_ & ((alignsize) - 1)) { \
+ pr_info(#name " must align to %d\n", \
+ (alignsize)); \
+ _##name_ = ALIGN(name, (alignsize)); \
+ } \
+ name = _##name_ / 1024; \
+ pstore_zone_info->name = _##name_; \
+ }
+
+ verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
+#undef verify_size
+
+ pstore_zone_info->total_size = dev->total_size;
+ pstore_zone_info->max_reason = max_reason;
+ pstore_zone_info->read = dev->read;
+ pstore_zone_info->write = dev->write;
+ pstore_zone_info->panic_write = dev->panic_write;
+ pstore_zone_info->name = KBUILD_MODNAME;
+ pstore_zone_info->owner = THIS_MODULE;
+
+ ret = register_pstore_zone(pstore_zone_info);
+ if (ret) {
+ kfree(pstore_zone_info);
+ pstore_zone_info = NULL;
+ }
+ mutex_unlock(&pstore_blk_lock);
+ return ret;
+}
+
+static void psblk_unregister_do(struct pstore_device_info *dev)
+{
+ mutex_lock(&pstore_blk_lock);
+ if (pstore_zone_info && pstore_zone_info->read == dev->read) {
+ unregister_pstore_zone(pstore_zone_info);
+ kfree(pstore_zone_info);
+ pstore_zone_info = NULL;
+ }
+ mutex_unlock(&pstore_blk_lock);
+}
+
+/**
+ * psblk_get_bdev() - open block device
+ *
+ * @holder: Exclusive holder identifier
+ * @info: Information about bdev to fill in
+ *
+ * Return: pointer to block device on success and others on error.
+ *
+ * On success, the returned block_device has reference count of one.
+ */
+static struct block_device *psblk_get_bdev(void *holder,
+ struct bdev_info *info)
+{
+ struct block_device *bdev = ERR_PTR(-ENODEV);
+ fmode_t mode = FMODE_READ | FMODE_WRITE;
+ sector_t nr_sects;
+
+ if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
+ return ERR_PTR(-EINVAL);
+
+ if (pstore_zone_info)
+ return ERR_PTR(-EBUSY);
+
+ if (!blkdev[0])
+ return ERR_PTR(-ENODEV);
+
+ if (holder)
+ mode |= FMODE_EXCL;
+ bdev = blkdev_get_by_path(blkdev, mode, holder);
+ if (IS_ERR(bdev)) {
+ dev_t devt;
+
+ devt = name_to_dev_t(blkdev);
+ if (devt == 0)
+ return ERR_PTR(-ENODEV);
+ bdev = blkdev_get_by_dev(devt, mode, holder);
+ }
+
+ nr_sects = part_nr_sects_read(bdev->bd_part);
+ if (!nr_sects) {
+ pr_err("not enough space for '%s'\n", blkdev);
+ blkdev_put(bdev, mode);
+ return ERR_PTR(-ENOSPC);
+ }
+
+ if (info) {
+ info->devt = bdev->bd_dev;
+ info->nr_sects = nr_sects;
+ info->start_sect = get_start_sect(bdev);
+ }
+
+ return bdev;
+}
+
+static void psblk_put_bdev(struct block_device *bdev, void *holder)
+{
+ fmode_t mode = FMODE_READ | FMODE_WRITE;
+
+ if (!bdev)
+ return;
+
+ if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
+ return;
+
+ if (holder)
+ mode |= FMODE_EXCL;
+ blkdev_put(bdev, mode);
+}
+
+static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
+{
+ struct block_device *bdev = psblk_bdev;
+ struct file file;
+ struct kiocb kiocb;
+ struct iov_iter iter;
+ struct kvec iov = {.iov_base = buf, .iov_len = bytes};
+
+ if (!bdev)
+ return -ENODEV;
+
+ memset(&file, 0, sizeof(struct file));
+ file.f_mapping = bdev->bd_inode->i_mapping;
+ file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
+ file.f_inode = bdev->bd_inode;
+ file_ra_state_init(&file.f_ra, file.f_mapping);
+
+ init_sync_kiocb(&kiocb, &file);
+ kiocb.ki_pos = pos;
+ iov_iter_kvec(&iter, READ, &iov, 1, bytes);
+
+ return generic_file_read_iter(&kiocb, &iter);
+}
+
+static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
+ loff_t pos)
+{
+ struct block_device *bdev = psblk_bdev;
+ struct iov_iter iter;
+ struct kiocb kiocb;
+ struct file file;
+ ssize_t ret;
+ struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
+
+ if (!bdev)
+ return -ENODEV;
+
+ /* Console/Ftrace backend may handle buffer until flush dirty zones */
+ if (in_interrupt() || irqs_disabled())
+ return -EBUSY;
+
+ memset(&file, 0, sizeof(struct file));
+ file.f_mapping = bdev->bd_inode->i_mapping;
+ file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
+ file.f_inode = bdev->bd_inode;
+
+ init_sync_kiocb(&kiocb, &file);
+ kiocb.ki_pos = pos;
+ iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
+
+ inode_lock(bdev->bd_inode);
+ ret = generic_write_checks(&kiocb, &iter);
+ if (ret > 0)
+ ret = generic_perform_write(&file, &iter, pos);
+ inode_unlock(bdev->bd_inode);
+
+ if (likely(ret > 0)) {
+ const struct file_operations f_op = {.fsync = blkdev_fsync};
+
+ file.f_op = &f_op;
+ kiocb.ki_pos += ret;
+ ret = generic_write_sync(&kiocb, ret);
+ }
+ return ret;
+}
+
+static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
+ loff_t off)
+{
+ int ret;
+
+ if (!blkdev_panic_write)
+ return -EOPNOTSUPP;
+
+ /* size and off must align to SECTOR_SIZE for block device */
+ ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
+ size >> SECTOR_SHIFT);
+ return ret ? -EIO : size;
+}
+
+static int __register_pstore_blk(struct pstore_blk_info *info)
+{
+ char bdev_name[BDEVNAME_SIZE];
+ struct block_device *bdev;
+ struct pstore_device_info dev;
+ struct bdev_info binfo;
+ void *holder = blkdev;
+ int ret = -ENODEV;
+
+ if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
+ return -EINVAL;
+
+ /* hold bdev exclusively */
+ memset(&binfo, 0, sizeof(binfo));
+ bdev = psblk_get_bdev(holder, &binfo);
+ if (IS_ERR(bdev)) {
+ pr_err("failed to open '%s'!\n", blkdev);
+ ret = PTR_ERR(bdev);
+ goto err_put_bdev;
+ }
+
+ /* only allow driver matching the @blkdev */
+ if (!binfo.devt || MAJOR(binfo.devt) != info->major) {
+ pr_debug("invalid major %u (expect %u)\n",
+ info->major, MAJOR(binfo.devt));
+ ret = -ENODEV;
+ goto err_put_bdev;
+ }
+
+ /* psblk_bdev must be assigned before register to pstore/blk */
+ psblk_bdev = bdev;
+ blkdev_panic_write = info->panic_write;
+
+ /* Copy back block device details. */
+ info->devt = binfo.devt;
+ info->nr_sects = binfo.nr_sects;
+ info->start_sect = binfo.start_sect;
+
+ memset(&dev, 0, sizeof(dev));
+ dev.total_size = info->nr_sects << SECTOR_SHIFT;
+ dev.flags = info->flags;
+ dev.read = psblk_generic_blk_read;
+ dev.write = psblk_generic_blk_write;
+ dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
+
+ ret = psblk_register_do(&dev);
+ if (ret)
+ goto err_put_bdev;
+
+ bdevname(bdev, bdev_name);
+ pr_info("attached %s%s\n", bdev_name,
+ info->panic_write ? "" : " (no dedicated panic_write!)");
+ return 0;
+
+err_put_bdev:
+ psblk_bdev = NULL;
+ blkdev_panic_write = NULL;
+ psblk_put_bdev(bdev, holder);
+ return ret;
+}
+
+/**
+ * register_pstore_blk() - register block device to pstore/blk
+ *
+ * @info: details on the desired block device interface
+ *
+ * Return:
+ * * 0 - OK
+ * * Others - something error.
+ */
+int register_pstore_blk(struct pstore_blk_info *info)
+{
+ int ret;
+
+ mutex_lock(&pstore_blk_lock);
+ ret = __register_pstore_blk(info);
+ mutex_unlock(&pstore_blk_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_pstore_blk);
+
+static void __unregister_pstore_blk(unsigned int major)
+{
+ struct pstore_device_info dev = { .read = psblk_generic_blk_read };
+ void *holder = blkdev;
+
+ WARN_ON(!mutex_is_locked(&pstore_blk_lock));
+ if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
+ psblk_unregister_do(&dev);
+ psblk_put_bdev(psblk_bdev, holder);
+ blkdev_panic_write = NULL;
+ psblk_bdev = NULL;
+ memset(&g_bdev_info, 0, sizeof(g_bdev_info));
+ }
+}
+
+/**
+ * unregister_pstore_blk() - unregister block device from pstore/blk
+ *
+ * @major: the major device number of device
+ */
+void unregister_pstore_blk(unsigned int major)
+{
+ mutex_lock(&pstore_blk_lock);
+ __unregister_pstore_blk(major);
+ mutex_unlock(&pstore_blk_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_pstore_blk);
+
+static void __exit pstore_blk_exit(void)
+{
+ mutex_lock(&pstore_blk_lock);
+ if (psblk_bdev)
+ __unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
+ mutex_unlock(&pstore_blk_lock);
+}
+module_exit(pstore_blk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
+MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
+MODULE_DESCRIPTION("pstore backend for block devices");
diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
new file mode 100644
index 000000000000..4501977b1336
--- /dev/null
+++ b/include/linux/pstore_blk.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PSTORE_BLK_H_
+#define __PSTORE_BLK_H_
+
+#include <linux/types.h>
+#include <linux/pstore.h>
+#include <linux/pstore_zone.h>
+
+/**
+ * typedef pstore_blk_panic_write_op - panic write operation to block device
+ *
+ * @buf: the data to write
+ * @start_sect: start sector to block device
+ * @sects: sectors count on buf
+ *
+ * Return: On success, zero should be returned. Others mean error.
+ *
+ * Panic write to block device must be aligned to SECTOR_SIZE.
+ */
+typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
+ sector_t sects);
+
+/**
+ * struct pstore_blk_info - pstore/blk registration details
+ *
+ * @major: Which major device number to support with pstore/blk
+ * @flags: The supported PSTORE_FLAGS_* from linux/pstore.h.
+ * @panic_write:The write operation only used for the panic case.
+ * This can be NULL, but is recommended to avoid losing
+ * crash data if the kernel's IO path or work queues are
+ * broken during a panic.
+ * @devt: The dev_t that pstore/blk has attached to.
+ * @nr_sects: Number of sectors on @devt.
+ * @start_sect: Starting sector on @devt.
+ */
+struct pstore_blk_info {
+ unsigned int major;
+ unsigned int flags;
+ pstore_blk_panic_write_op panic_write;
+
+ /* Filled in by pstore/blk after registration. */
+ dev_t devt;
+ sector_t nr_sects;
+ sector_t start_sect;
+};
+
+int register_pstore_blk(struct pstore_blk_info *info);
+void unregister_pstore_blk(unsigned int major);
+
+#endif
--
2.20.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 09/18] pstore/blk: Introduce backend for block devices
@ 2020-05-10 20:24 ` Kees Cook
0 siblings, 0 replies; 11+ messages in thread
From: Kees Cook @ 2020-05-10 20:24 UTC (permalink / raw)
To: WeiXiong Liao
Cc: Petr Mladek, Tony Luck, Kees Cook, Jonathan Corbet,
Richard Weinberger, Anton Vorontsov, linux-doc, linux-kernel,
Steven Rostedt, Sergey Senozhatsky, Colin Cross, linux-mtd,
Miquel Raynal, Pavel Tatashin, Rob Herring, Vignesh Raghavendra
From: WeiXiong Liao <liaoweixiong@allwinnertech.com>
pstore/blk is similar to pstore/ram, but uses a block device as the
storage rather than persistent ram.
The pstore/blk backend solves two common use-cases that used to preclude
using pstore/ram:
- not all devices have a battery that could be used to persist
regular RAM across power failures.
- most embedded intelligent equipment have no persistent ram, which
increases costs, instead preferring cheaper solutions, like block
devices.
pstore/blk provides separate configurations for the end user and for the
block drivers. User configuration determines how pstore/blk operates, such
as record sizes, max kmsg dump reasons, etc. These can be set by Kconfig
and/or module parameters, but module parameter have priority over Kconfig.
Driver configuration covers all the details about the target block device,
such as total size of the device and how to perform read/write operations.
These are provided by block drivers, calling pstore_register_blkdev(),
including an optional panic_write callback used to bypass regular IO
APIs in an effort to avoid potentially destabilized kernel code during
a panic.
Signed-off-by: WeiXiong Liao <liaoweixiong@allwinnertech.com>
Link: https://lore.kernel.org/r/1585126506-18635-3-git-send-email-liaoweixiong@allwinnertech.com
Co-developed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
fs/pstore/Kconfig | 64 ++++++
fs/pstore/Makefile | 3 +
fs/pstore/blk.c | 436 +++++++++++++++++++++++++++++++++++++
include/linux/pstore_blk.h | 51 +++++
4 files changed, 554 insertions(+)
create mode 100644 fs/pstore/blk.c
create mode 100644 include/linux/pstore_blk.h
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index 98d2457bdd9f..92ba73bd0b62 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -160,3 +160,67 @@ config PSTORE_ZONE
help
The common layer for pstore/blk (and pstore/ram in the future)
to manage storage in zones.
+
+config PSTORE_BLK
+ tristate "Log panic/oops to a block device"
+ depends on PSTORE
+ depends on BLOCK
+ select PSTORE_ZONE
+ default n
+ help
+ This enables panic and oops message to be logged to a block dev
+ where it can be read back at some later point.
+
+ If unsure, say N.
+
+config PSTORE_BLK_BLKDEV
+ string "block device identifier"
+ depends on PSTORE_BLK
+ default ""
+ help
+ Which block device should be used for pstore/blk.
+
+ It accept the following variants:
+ 1) <hex_major><hex_minor> device number in hexadecimal represents
+ itself no leading 0x, for example b302.
+ 2) /dev/<disk_name> represents the device number of disk
+ 3) /dev/<disk_name><decimal> represents the device number
+ of partition - device number of disk plus the partition number
+ 4) /dev/<disk_name>p<decimal> - same as the above, this form is
+ used when disk name of partitioned disk ends with a digit.
+ 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
+ unique id of a partition if the partition table provides it.
+ The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
+ partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
+ filled hex representation of the 32-bit "NT disk signature", and PP
+ is a zero-filled hex representation of the 1-based partition number.
+ 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
+ to a partition with a known unique id.
+ 7) <major>:<minor> major and minor number of the device separated by
+ a colon.
+
+ NOTE that, both Kconfig and module parameters can configure
+ pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_KMSG_SIZE
+ int "Size in Kbytes of kmsg dump log to store"
+ depends on PSTORE_BLK
+ default 64
+ help
+ This just sets size of kmsg dump (oops, panic, etc) log for
+ pstore/blk. The size is in KB and must be a multiple of 4.
+
+ NOTE that, both Kconfig and module parameters can configure
+ pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_MAX_REASON
+ int "Maximum kmsg dump reason to store"
+ depends on PSTORE_BLK
+ default 2
+ help
+ The maximum reason for kmsg dumps to store. The default is
+ 2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
+ enum kmsg_dump_reason for more details.
+
+ NOTE that, both Kconfig and module parameters can configure
+ pstore/blk, but module parameters have priority over Kconfig.
diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
index 58a967cbe4af..c270467aeece 100644
--- a/fs/pstore/Makefile
+++ b/fs/pstore/Makefile
@@ -15,3 +15,6 @@ obj-$(CONFIG_PSTORE_RAM) += ramoops.o
pstore_zone-objs += zone.o
obj-$(CONFIG_PSTORE_ZONE) += pstore_zone.o
+
+pstore_blk-objs += blk.o
+obj-$(CONFIG_PSTORE_BLK) += pstore_blk.o
diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c
new file mode 100644
index 000000000000..cec1fa261d1b
--- /dev/null
+++ b/fs/pstore/blk.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements pstore backend driver that write to block (or non-block) storage
+ * devices, using the pstore/zone API.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "../../block/blk.h"
+#include <linux/blkdev.h>
+#include <linux/string.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pstore_blk.h>
+#include <linux/mount.h>
+#include <linux/uio.h>
+
+static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
+module_param(kmsg_size, long, 0400);
+MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
+
+static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
+module_param(max_reason, int, 0400);
+MODULE_PARM_DESC(max_reason,
+ "maximum reason for kmsg dump (default 2: Oops and Panic)");
+
+/*
+ * blkdev - the block device to use for pstore storage
+ *
+ * Usually, this will be a partition of a block device.
+ *
+ * blkdev accepts the following variants:
+ * 1) <hex_major><hex_minor> device number in hexadecimal represents itself
+ * no leading 0x, for example b302.
+ * 2) /dev/<disk_name> represents the device number of disk
+ * 3) /dev/<disk_name><decimal> represents the device number
+ * of partition - device number of disk plus the partition number
+ * 4) /dev/<disk_name>p<decimal> - same as the above, that form is
+ * used when disk name of partitioned disk ends on a digit.
+ * 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
+ * unique id of a partition if the partition table provides it.
+ * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
+ * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
+ * filled hex representation of the 32-bit "NT disk signature", and PP
+ * is a zero-filled hex representation of the 1-based partition number.
+ * 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
+ * a partition with a known unique id.
+ * 7) <major>:<minor> major and minor number of the device separated by
+ * a colon.
+ */
+static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
+module_param_string(blkdev, blkdev, 80, 0400);
+MODULE_PARM_DESC(blkdev, "block device for pstore storage");
+
+/*
+ * All globals must only be accessed under the pstore_blk_lock
+ * during the register/unregister functions.
+ */
+static DEFINE_MUTEX(pstore_blk_lock);
+static struct block_device *psblk_bdev;
+static struct pstore_zone_info *pstore_zone_info;
+static pstore_blk_panic_write_op blkdev_panic_write;
+static struct bdev_info {
+ dev_t devt;
+ sector_t nr_sects;
+ sector_t start_sect;
+} g_bdev_info;
+
+/**
+ * struct pstore_device_info - back-end pstore/blk driver structure.
+ *
+ * @total_size: The total size in bytes pstore/blk can use. It must be greater
+ * than 4096 and be multiple of 4096.
+ * @flags: Refer to macro starting with PSTORE_FLAGS defined in
+ * linux/pstore.h. It means what front-ends this device support.
+ * Zero means all backends for compatible.
+ * @read: The general read operation. Both of the function parameters
+ * @size and @offset are relative value to bock device (not the
+ * whole disk).
+ * On success, the number of bytes should be returned, others
+ * means error.
+ * @write: The same as @read.
+ * @panic_write:The write operation only used for panic case. It's optional
+ * if you do not care panic log. The parameters and return value
+ * are the same as @read.
+ */
+struct pstore_device_info {
+ unsigned long total_size;
+ unsigned int flags;
+ pstore_zone_read_op read;
+ pstore_zone_write_op write;
+ pstore_zone_write_op panic_write;
+};
+
+static int psblk_register_do(struct pstore_device_info *dev)
+{
+ int ret;
+
+ if (!dev || !dev->total_size || !dev->read || !dev->write)
+ return -EINVAL;
+
+ mutex_lock(&pstore_blk_lock);
+
+ /* someone already registered before */
+ if (pstore_zone_info) {
+ mutex_unlock(&pstore_blk_lock);
+ return -EBUSY;
+ }
+ pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
+ if (!pstore_zone_info) {
+ mutex_unlock(&pstore_blk_lock);
+ return -ENOMEM;
+ }
+
+ /* zero means not limit on which backends to attempt to store. */
+ if (!dev->flags)
+ dev->flags = UINT_MAX;
+
+#define verify_size(name, alignsize, enabled) { \
+ long _##name_ = (enabled) ? (name) : 0; \
+ _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024); \
+ if (_##name_ & ((alignsize) - 1)) { \
+ pr_info(#name " must align to %d\n", \
+ (alignsize)); \
+ _##name_ = ALIGN(name, (alignsize)); \
+ } \
+ name = _##name_ / 1024; \
+ pstore_zone_info->name = _##name_; \
+ }
+
+ verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
+#undef verify_size
+
+ pstore_zone_info->total_size = dev->total_size;
+ pstore_zone_info->max_reason = max_reason;
+ pstore_zone_info->read = dev->read;
+ pstore_zone_info->write = dev->write;
+ pstore_zone_info->panic_write = dev->panic_write;
+ pstore_zone_info->name = KBUILD_MODNAME;
+ pstore_zone_info->owner = THIS_MODULE;
+
+ ret = register_pstore_zone(pstore_zone_info);
+ if (ret) {
+ kfree(pstore_zone_info);
+ pstore_zone_info = NULL;
+ }
+ mutex_unlock(&pstore_blk_lock);
+ return ret;
+}
+
+static void psblk_unregister_do(struct pstore_device_info *dev)
+{
+ mutex_lock(&pstore_blk_lock);
+ if (pstore_zone_info && pstore_zone_info->read == dev->read) {
+ unregister_pstore_zone(pstore_zone_info);
+ kfree(pstore_zone_info);
+ pstore_zone_info = NULL;
+ }
+ mutex_unlock(&pstore_blk_lock);
+}
+
+/**
+ * psblk_get_bdev() - open block device
+ *
+ * @holder: Exclusive holder identifier
+ * @info: Information about bdev to fill in
+ *
+ * Return: pointer to block device on success and others on error.
+ *
+ * On success, the returned block_device has reference count of one.
+ */
+static struct block_device *psblk_get_bdev(void *holder,
+ struct bdev_info *info)
+{
+ struct block_device *bdev = ERR_PTR(-ENODEV);
+ fmode_t mode = FMODE_READ | FMODE_WRITE;
+ sector_t nr_sects;
+
+ if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
+ return ERR_PTR(-EINVAL);
+
+ if (pstore_zone_info)
+ return ERR_PTR(-EBUSY);
+
+ if (!blkdev[0])
+ return ERR_PTR(-ENODEV);
+
+ if (holder)
+ mode |= FMODE_EXCL;
+ bdev = blkdev_get_by_path(blkdev, mode, holder);
+ if (IS_ERR(bdev)) {
+ dev_t devt;
+
+ devt = name_to_dev_t(blkdev);
+ if (devt == 0)
+ return ERR_PTR(-ENODEV);
+ bdev = blkdev_get_by_dev(devt, mode, holder);
+ }
+
+ nr_sects = part_nr_sects_read(bdev->bd_part);
+ if (!nr_sects) {
+ pr_err("not enough space for '%s'\n", blkdev);
+ blkdev_put(bdev, mode);
+ return ERR_PTR(-ENOSPC);
+ }
+
+ if (info) {
+ info->devt = bdev->bd_dev;
+ info->nr_sects = nr_sects;
+ info->start_sect = get_start_sect(bdev);
+ }
+
+ return bdev;
+}
+
+static void psblk_put_bdev(struct block_device *bdev, void *holder)
+{
+ fmode_t mode = FMODE_READ | FMODE_WRITE;
+
+ if (!bdev)
+ return;
+
+ if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
+ return;
+
+ if (holder)
+ mode |= FMODE_EXCL;
+ blkdev_put(bdev, mode);
+}
+
+static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
+{
+ struct block_device *bdev = psblk_bdev;
+ struct file file;
+ struct kiocb kiocb;
+ struct iov_iter iter;
+ struct kvec iov = {.iov_base = buf, .iov_len = bytes};
+
+ if (!bdev)
+ return -ENODEV;
+
+ memset(&file, 0, sizeof(struct file));
+ file.f_mapping = bdev->bd_inode->i_mapping;
+ file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
+ file.f_inode = bdev->bd_inode;
+ file_ra_state_init(&file.f_ra, file.f_mapping);
+
+ init_sync_kiocb(&kiocb, &file);
+ kiocb.ki_pos = pos;
+ iov_iter_kvec(&iter, READ, &iov, 1, bytes);
+
+ return generic_file_read_iter(&kiocb, &iter);
+}
+
+static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
+ loff_t pos)
+{
+ struct block_device *bdev = psblk_bdev;
+ struct iov_iter iter;
+ struct kiocb kiocb;
+ struct file file;
+ ssize_t ret;
+ struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
+
+ if (!bdev)
+ return -ENODEV;
+
+ /* Console/Ftrace backend may handle buffer until flush dirty zones */
+ if (in_interrupt() || irqs_disabled())
+ return -EBUSY;
+
+ memset(&file, 0, sizeof(struct file));
+ file.f_mapping = bdev->bd_inode->i_mapping;
+ file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
+ file.f_inode = bdev->bd_inode;
+
+ init_sync_kiocb(&kiocb, &file);
+ kiocb.ki_pos = pos;
+ iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
+
+ inode_lock(bdev->bd_inode);
+ ret = generic_write_checks(&kiocb, &iter);
+ if (ret > 0)
+ ret = generic_perform_write(&file, &iter, pos);
+ inode_unlock(bdev->bd_inode);
+
+ if (likely(ret > 0)) {
+ const struct file_operations f_op = {.fsync = blkdev_fsync};
+
+ file.f_op = &f_op;
+ kiocb.ki_pos += ret;
+ ret = generic_write_sync(&kiocb, ret);
+ }
+ return ret;
+}
+
+static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
+ loff_t off)
+{
+ int ret;
+
+ if (!blkdev_panic_write)
+ return -EOPNOTSUPP;
+
+ /* size and off must align to SECTOR_SIZE for block device */
+ ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
+ size >> SECTOR_SHIFT);
+ return ret ? -EIO : size;
+}
+
+static int __register_pstore_blk(struct pstore_blk_info *info)
+{
+ char bdev_name[BDEVNAME_SIZE];
+ struct block_device *bdev;
+ struct pstore_device_info dev;
+ struct bdev_info binfo;
+ void *holder = blkdev;
+ int ret = -ENODEV;
+
+ if (WARN_ON(!mutex_is_locked(&pstore_blk_lock)))
+ return -EINVAL;
+
+ /* hold bdev exclusively */
+ memset(&binfo, 0, sizeof(binfo));
+ bdev = psblk_get_bdev(holder, &binfo);
+ if (IS_ERR(bdev)) {
+ pr_err("failed to open '%s'!\n", blkdev);
+ ret = PTR_ERR(bdev);
+ goto err_put_bdev;
+ }
+
+ /* only allow driver matching the @blkdev */
+ if (!binfo.devt || MAJOR(binfo.devt) != info->major) {
+ pr_debug("invalid major %u (expect %u)\n",
+ info->major, MAJOR(binfo.devt));
+ ret = -ENODEV;
+ goto err_put_bdev;
+ }
+
+ /* psblk_bdev must be assigned before register to pstore/blk */
+ psblk_bdev = bdev;
+ blkdev_panic_write = info->panic_write;
+
+ /* Copy back block device details. */
+ info->devt = binfo.devt;
+ info->nr_sects = binfo.nr_sects;
+ info->start_sect = binfo.start_sect;
+
+ memset(&dev, 0, sizeof(dev));
+ dev.total_size = info->nr_sects << SECTOR_SHIFT;
+ dev.flags = info->flags;
+ dev.read = psblk_generic_blk_read;
+ dev.write = psblk_generic_blk_write;
+ dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
+
+ ret = psblk_register_do(&dev);
+ if (ret)
+ goto err_put_bdev;
+
+ bdevname(bdev, bdev_name);
+ pr_info("attached %s%s\n", bdev_name,
+ info->panic_write ? "" : " (no dedicated panic_write!)");
+ return 0;
+
+err_put_bdev:
+ psblk_bdev = NULL;
+ blkdev_panic_write = NULL;
+ psblk_put_bdev(bdev, holder);
+ return ret;
+}
+
+/**
+ * register_pstore_blk() - register block device to pstore/blk
+ *
+ * @info: details on the desired block device interface
+ *
+ * Return:
+ * * 0 - OK
+ * * Others - something error.
+ */
+int register_pstore_blk(struct pstore_blk_info *info)
+{
+ int ret;
+
+ mutex_lock(&pstore_blk_lock);
+ ret = __register_pstore_blk(info);
+ mutex_unlock(&pstore_blk_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_pstore_blk);
+
+static void __unregister_pstore_blk(unsigned int major)
+{
+ struct pstore_device_info dev = { .read = psblk_generic_blk_read };
+ void *holder = blkdev;
+
+ WARN_ON(!mutex_is_locked(&pstore_blk_lock));
+ if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
+ psblk_unregister_do(&dev);
+ psblk_put_bdev(psblk_bdev, holder);
+ blkdev_panic_write = NULL;
+ psblk_bdev = NULL;
+ memset(&g_bdev_info, 0, sizeof(g_bdev_info));
+ }
+}
+
+/**
+ * unregister_pstore_blk() - unregister block device from pstore/blk
+ *
+ * @major: the major device number of device
+ */
+void unregister_pstore_blk(unsigned int major)
+{
+ mutex_lock(&pstore_blk_lock);
+ __unregister_pstore_blk(major);
+ mutex_unlock(&pstore_blk_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_pstore_blk);
+
+static void __exit pstore_blk_exit(void)
+{
+ mutex_lock(&pstore_blk_lock);
+ if (psblk_bdev)
+ __unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
+ mutex_unlock(&pstore_blk_lock);
+}
+module_exit(pstore_blk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
+MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
+MODULE_DESCRIPTION("pstore backend for block devices");
diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
new file mode 100644
index 000000000000..4501977b1336
--- /dev/null
+++ b/include/linux/pstore_blk.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PSTORE_BLK_H_
+#define __PSTORE_BLK_H_
+
+#include <linux/types.h>
+#include <linux/pstore.h>
+#include <linux/pstore_zone.h>
+
+/**
+ * typedef pstore_blk_panic_write_op - panic write operation to block device
+ *
+ * @buf: the data to write
+ * @start_sect: start sector to block device
+ * @sects: sectors count on buf
+ *
+ * Return: On success, zero should be returned. Others mean error.
+ *
+ * Panic write to block device must be aligned to SECTOR_SIZE.
+ */
+typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
+ sector_t sects);
+
+/**
+ * struct pstore_blk_info - pstore/blk registration details
+ *
+ * @major: Which major device number to support with pstore/blk
+ * @flags: The supported PSTORE_FLAGS_* from linux/pstore.h.
+ * @panic_write:The write operation only used for the panic case.
+ * This can be NULL, but is recommended to avoid losing
+ * crash data if the kernel's IO path or work queues are
+ * broken during a panic.
+ * @devt: The dev_t that pstore/blk has attached to.
+ * @nr_sects: Number of sectors on @devt.
+ * @start_sect: Starting sector on @devt.
+ */
+struct pstore_blk_info {
+ unsigned int major;
+ unsigned int flags;
+ pstore_blk_panic_write_op panic_write;
+
+ /* Filled in by pstore/blk after registration. */
+ dev_t devt;
+ sector_t nr_sects;
+ sector_t start_sect;
+};
+
+int register_pstore_blk(struct pstore_blk_info *info);
+void unregister_pstore_blk(unsigned int major);
+
+#endif
--
2.20.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2020-05-11 23:11 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-10 22:14 [PATCH v7 09/18] pstore/blk: Introduce backend for block devices kbuild test robot
-- strict thread matches above, loose matches on Subject: below --
2020-05-10 20:24 [PATCH v7 00/18] pstore: mtd: support crash log to block and mtd device Kees Cook
2020-05-10 20:24 ` [PATCH v7 09/18] pstore/blk: Introduce backend for block devices Kees Cook
2020-05-10 20:24 ` Kees Cook
2020-05-11 8:36 ` WeiXiong Liao
2020-05-11 8:36 ` WeiXiong Liao
2020-05-11 23:08 ` Kees Cook
2020-05-11 23:08 ` Kees Cook
2020-05-11 15:36 ` Randy Dunlap
2020-05-11 15:36 ` Randy Dunlap
2020-05-11 23:11 ` Kees Cook
2020-05-11 23:11 ` Kees Cook
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.