Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[XFS] handle memory allocation failures during log initialisation

When there is no memory left in the system, xfs_buf_get_noaddr()
can fail. If this happens at mount time during xlog_alloc_log()
we fail to catch the error and oops.

Catch the error from xfs_buf_get_noaddr(), and allow other memory
allocations to fail and catch those errors too. Report the error
to the console and fail the mount with ENOMEM.

Tested by manually injecting errors into xfs_buf_get_noaddr() and
xlog_alloc_log().

Version 2:
o remove unnecessary casts of the returned pointer from kmem_zalloc()

SGI-PV: 987246

Signed-off-by: Dave Chinner <david@fromorbit.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>

authored by

Dave Chinner and committed by
Lachlan McIlroy
644c3567 dcd7b4e5

+36 -3
+36 -3
fs/xfs/xfs_log.c
··· 563 563 } 564 564 565 565 mp->m_log = xlog_alloc_log(mp, log_target, blk_offset, num_bblks); 566 + if (!mp->m_log) { 567 + cmn_err(CE_WARN, "XFS: Log allocation failed: No memory!"); 568 + error = ENOMEM; 569 + goto out; 570 + } 566 571 567 572 /* 568 573 * Initialize the AIL now we have a log. ··· 606 601 return 0; 607 602 error: 608 603 xfs_log_unmount_dealloc(mp); 604 + out: 609 605 return error; 610 606 } /* xfs_log_mount */ 611 607 ··· 1223 1217 int i; 1224 1218 int iclogsize; 1225 1219 1226 - log = (xlog_t *)kmem_zalloc(sizeof(xlog_t), KM_SLEEP); 1220 + log = kmem_zalloc(sizeof(xlog_t), KM_MAYFAIL); 1221 + if (!log) 1222 + return NULL; 1227 1223 1228 1224 log->l_mp = mp; 1229 1225 log->l_targ = log_target; ··· 1257 1249 xlog_get_iclog_buffer_size(mp, log); 1258 1250 1259 1251 bp = xfs_buf_get_empty(log->l_iclog_size, mp->m_logdev_targp); 1252 + if (!bp) 1253 + goto out_free_log; 1260 1254 XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone); 1261 1255 XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb); 1262 1256 XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1); ··· 1285 1275 iclogsize = log->l_iclog_size; 1286 1276 ASSERT(log->l_iclog_size >= 4096); 1287 1277 for (i=0; i < log->l_iclog_bufs; i++) { 1288 - *iclogp = (xlog_in_core_t *) 1289 - kmem_zalloc(sizeof(xlog_in_core_t), KM_SLEEP); 1278 + *iclogp = kmem_zalloc(sizeof(xlog_in_core_t), KM_MAYFAIL); 1279 + if (!*iclogp) 1280 + goto out_free_iclog; 1281 + 1290 1282 iclog = *iclogp; 1291 1283 iclog->ic_prev = prev_iclog; 1292 1284 prev_iclog = iclog; 1293 1285 1294 1286 bp = xfs_buf_get_noaddr(log->l_iclog_size, mp->m_logdev_targp); 1287 + if (!bp) 1288 + goto out_free_iclog; 1295 1289 if (!XFS_BUF_CPSEMA(bp)) 1296 1290 ASSERT(0); 1297 1291 XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone); ··· 1337 1323 log->l_iclog->ic_prev = prev_iclog; /* re-write 1st prev ptr */ 1338 1324 1339 1325 return log; 1326 + 1327 + out_free_iclog: 1328 + for (iclog = log->l_iclog; iclog; iclog = prev_iclog) { 1329 + prev_iclog = iclog->ic_next; 1330 + if (iclog->ic_bp) { 1331 + sv_destroy(&iclog->ic_force_wait); 1332 + sv_destroy(&iclog->ic_write_wait); 1333 + xfs_buf_free(iclog->ic_bp); 1334 + xlog_trace_iclog_dealloc(iclog); 1335 + } 1336 + kmem_free(iclog); 1337 + } 1338 + spinlock_destroy(&log->l_icloglock); 1339 + spinlock_destroy(&log->l_grant_lock); 1340 + xlog_trace_loggrant_dealloc(log); 1341 + xfs_buf_free(log->l_xbuf); 1342 + out_free_log: 1343 + kmem_free(log); 1344 + return NULL; 1340 1345 } /* xlog_alloc_log */ 1341 1346 1342 1347